From 194d12b304cd0ec68f79235beea0a7fb2cbb16b9 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Tue, 14 Jan 2025 10:58:46 -0800 Subject: [PATCH 001/107] [bugfix] fix streaming GeneratorExit exception with LlamaStackAsLibraryClient (#760) # What does this PR do? #### Issue - Using Jupyter notebook with LlamaStackAsLibraryClient + streaming gives exception ``` Exception ignored in: Traceback (most recent call last): File "/opt/anaconda3/envs/fresh/lib/python3.11/site-packages/httpcore/_async/connection_pool.py", line 404, in _aiter_ yield part RuntimeError: async generator ignored GeneratorExit ``` - Reproduce w/ https://github.com/meta-llama/llama-stack/blob/notebook-streaming-debug/inline.ipynb #### Fix - Issue likely comes from stream_across_asyncio_run_boundary closing connection too soon when interacting in jupyter environment - This uses an alternative way to convert AsyncStream to SyncStream return type by sync version of LlamaStackAsLibraryClient, which calls AsyncLlamaStackAsLibraryClient calling async impls under the hood #### Additional changes - Moved tracing logic into AsyncLlamaStackAsLibraryClient.request s.t. streaming / non-streaming request for LlamaStackAsLibraryClient shares same code ## Test Plan - Test w/ together & fireworks & ollama with streaming and non-streaming using notebook in: https://github.com/meta-llama/llama-stack/blob/notebook-streaming-debug/inline.ipynb - Note: need to restart kernel and run pip install -e . in jupyter interpreter for local code change to take effect image ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- llama_stack/distribution/library_client.py | 130 +++++---------------- 1 file changed, 31 insertions(+), 99 deletions(-) diff --git a/llama_stack/distribution/library_client.py b/llama_stack/distribution/library_client.py index 50af2cdea..0c124e64b 100644 --- a/llama_stack/distribution/library_client.py +++ b/llama_stack/distribution/library_client.py @@ -9,12 +9,10 @@ import inspect import json import logging import os -import queue -import threading from concurrent.futures import ThreadPoolExecutor from enum import Enum from pathlib import Path -from typing import Any, Generator, get_args, get_origin, Optional, TypeVar +from typing import Any, get_args, get_origin, Optional, TypeVar import httpx import yaml @@ -64,71 +62,6 @@ def in_notebook(): return True -def stream_across_asyncio_run_boundary( - async_gen_maker, - pool_executor: ThreadPoolExecutor, - path: Optional[str] = None, - provider_data: Optional[dict[str, Any]] = None, -) -> Generator[T, None, None]: - result_queue = queue.Queue() - stop_event = threading.Event() - - async def consumer(): - # make sure we make the generator in the event loop context - gen = await async_gen_maker() - await start_trace(path, {"__location__": "library_client"}) - if provider_data: - set_request_provider_data( - {"X-LlamaStack-Provider-Data": json.dumps(provider_data)} - ) - try: - async for item in await gen: - result_queue.put(item) - except Exception as e: - print(f"Error in generator {e}") - result_queue.put(e) - except asyncio.CancelledError: - return - finally: - result_queue.put(StopIteration) - stop_event.set() - await end_trace() - - def run_async(): - # Run our own loop to avoid double async generator cleanup which is done - # by asyncio.run() - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - try: - task = loop.create_task(consumer()) - loop.run_until_complete(task) - finally: - # Handle pending tasks like a generator's athrow() - pending = asyncio.all_tasks(loop) - if pending: - loop.run_until_complete( - asyncio.gather(*pending, return_exceptions=True) - ) - loop.close() - - future = pool_executor.submit(run_async) - - try: - # yield results as they come in - while not stop_event.is_set() or not result_queue.empty(): - try: - item = result_queue.get(timeout=0.1) - if item is StopIteration: - break - if isinstance(item, Exception): - raise item - yield item - except queue.Empty: - continue - finally: - future.result() - - def convert_pydantic_to_json_value(value: Any) -> Any: if isinstance(value, Enum): return value.value @@ -184,7 +117,7 @@ class LlamaStackAsLibraryClient(LlamaStackClient): ): super().__init__() self.async_client = AsyncLlamaStackAsLibraryClient( - config_path_or_template_name, custom_provider_registry + config_path_or_template_name, custom_provider_registry, provider_data ) self.pool_executor = ThreadPoolExecutor(max_workers=4) self.skip_logger_removal = skip_logger_removal @@ -210,39 +143,30 @@ class LlamaStackAsLibraryClient(LlamaStackClient): root_logger.removeHandler(handler) print(f"Removed handler {handler.__class__.__name__} from root logger") - def _get_path( - self, - cast_to: Any, - options: Any, - *, - stream=False, - stream_cls=None, - ): - return options.url - def request(self, *args, **kwargs): - path = self._get_path(*args, **kwargs) if kwargs.get("stream"): - return stream_across_asyncio_run_boundary( - lambda: self.async_client.request(*args, **kwargs), - self.pool_executor, - path=path, - provider_data=self.provider_data, - ) - else: + # NOTE: We are using AsyncLlamaStackClient under the hood + # A new event loop is needed to convert the AsyncStream + # from async client into SyncStream return type for streaming + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) - async def _traced_request(): - if self.provider_data: - set_request_provider_data( - {"X-LlamaStack-Provider-Data": json.dumps(self.provider_data)} - ) - await start_trace(path, {"__location__": "library_client"}) + def sync_generator(): try: - return await self.async_client.request(*args, **kwargs) + async_stream = loop.run_until_complete( + self.async_client.request(*args, **kwargs) + ) + while True: + chunk = loop.run_until_complete(async_stream.__anext__()) + yield chunk + except StopAsyncIteration: + pass finally: - await end_trace() + loop.close() - return asyncio.run(_traced_request()) + return sync_generator() + else: + return asyncio.run(self.async_client.request(*args, **kwargs)) class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): @@ -250,9 +174,9 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): self, config_path_or_template_name: str, custom_provider_registry: Optional[ProviderRegistry] = None, + provider_data: Optional[dict[str, Any]] = None, ): super().__init__() - # when using the library client, we should not log to console since many # of our logs are intended for server-side usage current_sinks = os.environ.get("TELEMETRY_SINKS", "sqlite").split(",") @@ -273,6 +197,7 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): self.config_path_or_template_name = config_path_or_template_name self.config = config self.custom_provider_registry = custom_provider_registry + self.provider_data = provider_data async def initialize(self): try: @@ -329,17 +254,24 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): if not self.endpoint_impls: raise ValueError("Client not initialized") + if self.provider_data: + set_request_provider_data( + {"X-LlamaStack-Provider-Data": json.dumps(self.provider_data)} + ) + await start_trace(options.url, {"__location__": "library_client"}) if stream: - return self._call_streaming( + response = await self._call_streaming( cast_to=cast_to, options=options, stream_cls=stream_cls, ) else: - return await self._call_non_streaming( + response = await self._call_non_streaming( cast_to=cast_to, options=options, ) + await end_trace() + return response async def _call_non_streaming( self, From a174938fbd8768f920df98909e341c9b4f1a6a65 Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Tue, 14 Jan 2025 11:31:50 -0800 Subject: [PATCH 002/107] Fix telemetry to work on reinstantiating new lib cli (#761) # What does this PR do? Since we maintain global state in our telemetry pipeline, reinstantiating lib cli will cause us to add duplicate span processors causing sqlite to lock out because of constraint violations since we now have two span processor writing to sqlite. This PR changes the telemetry adapter for otel to only instantiate the provider once and add the span processsors only once. Also fixes an issue llama stack build ## Test Plan tested with notebook at https://colab.research.google.com/drive/1ck7hXQxRl6UvT-ijNRZ-gMZxH1G3cN2d#scrollTo=9496f75c --- llama_stack/cli/stack/build.py | 5 +- .../telemetry/meta_reference/telemetry.py | 53 ++++++++++--------- .../providers/utils/telemetry/tracing.py | 3 +- 3 files changed, 30 insertions(+), 31 deletions(-) diff --git a/llama_stack/cli/stack/build.py b/llama_stack/cli/stack/build.py index 084374c8a..85e6cb962 100644 --- a/llama_stack/cli/stack/build.py +++ b/llama_stack/cli/stack/build.py @@ -4,9 +4,7 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. import argparse - import importlib.resources - import os import shutil from functools import lru_cache @@ -14,14 +12,12 @@ from pathlib import Path from typing import List, Optional from llama_stack.cli.subcommand import Subcommand - from llama_stack.distribution.datatypes import ( BuildConfig, DistributionSpec, Provider, StackRunConfig, ) - from llama_stack.distribution.distribution import get_provider_registry from llama_stack.distribution.resolver import InvalidProviderError from llama_stack.distribution.utils.dynamic import instantiate_class_type @@ -296,6 +292,7 @@ class StackBuild(Subcommand): / f"templates/{template_name}/run.yaml" ) with importlib.resources.as_file(template_path) as path: + run_config_file = build_dir / f"{build_config.name}-run.yaml" shutil.copy(path, run_config_file) # Find all ${env.VARIABLE} patterns cprint("Build Successful!", color="green") diff --git a/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py b/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py index efc37b553..332a150cf 100644 --- a/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py +++ b/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py @@ -30,13 +30,10 @@ from llama_stack.apis.telemetry import ( Trace, UnstructuredLogEvent, ) - from llama_stack.distribution.datatypes import Api - from llama_stack.providers.inline.telemetry.meta_reference.console_span_processor import ( ConsoleSpanProcessor, ) - from llama_stack.providers.inline.telemetry.meta_reference.sqlite_span_processor import ( SQLiteSpanProcessor, ) @@ -52,6 +49,7 @@ _GLOBAL_STORAGE = { "up_down_counters": {}, } _global_lock = threading.Lock() +_TRACER_PROVIDER = None def string_to_trace_id(s: str) -> int: @@ -80,31 +78,34 @@ class TelemetryAdapter(TelemetryDatasetMixin, Telemetry): } ) - provider = TracerProvider(resource=resource) - trace.set_tracer_provider(provider) - if TelemetrySink.OTEL in self.config.sinks: - otlp_exporter = OTLPSpanExporter( - endpoint=self.config.otel_endpoint, - ) - span_processor = BatchSpanProcessor(otlp_exporter) - trace.get_tracer_provider().add_span_processor(span_processor) - metric_reader = PeriodicExportingMetricReader( - OTLPMetricExporter( + global _TRACER_PROVIDER + if _TRACER_PROVIDER is None: + provider = TracerProvider(resource=resource) + trace.set_tracer_provider(provider) + _TRACER_PROVIDER = provider + if TelemetrySink.OTEL in self.config.sinks: + otlp_exporter = OTLPSpanExporter( endpoint=self.config.otel_endpoint, ) - ) - metric_provider = MeterProvider( - resource=resource, metric_readers=[metric_reader] - ) - metrics.set_meter_provider(metric_provider) - self.meter = metrics.get_meter(__name__) - if TelemetrySink.SQLITE in self.config.sinks: - trace.get_tracer_provider().add_span_processor( - SQLiteSpanProcessor(self.config.sqlite_db_path) - ) - self.trace_store = SQLiteTraceStore(self.config.sqlite_db_path) - if TelemetrySink.CONSOLE in self.config.sinks: - trace.get_tracer_provider().add_span_processor(ConsoleSpanProcessor()) + span_processor = BatchSpanProcessor(otlp_exporter) + trace.get_tracer_provider().add_span_processor(span_processor) + metric_reader = PeriodicExportingMetricReader( + OTLPMetricExporter( + endpoint=self.config.otel_endpoint, + ) + ) + metric_provider = MeterProvider( + resource=resource, metric_readers=[metric_reader] + ) + metrics.set_meter_provider(metric_provider) + self.meter = metrics.get_meter(__name__) + if TelemetrySink.SQLITE in self.config.sinks: + trace.get_tracer_provider().add_span_processor( + SQLiteSpanProcessor(self.config.sqlite_db_path) + ) + self.trace_store = SQLiteTraceStore(self.config.sqlite_db_path) + if TelemetrySink.CONSOLE in self.config.sinks: + trace.get_tracer_provider().add_span_processor(ConsoleSpanProcessor()) self._lock = _global_lock async def initialize(self) -> None: diff --git a/llama_stack/providers/utils/telemetry/tracing.py b/llama_stack/providers/utils/telemetry/tracing.py index f304d58f6..d84024941 100644 --- a/llama_stack/providers/utils/telemetry/tracing.py +++ b/llama_stack/providers/utils/telemetry/tracing.py @@ -127,7 +127,8 @@ class TraceContext: def setup_logger(api: Telemetry, level: int = logging.INFO): global BACKGROUND_LOGGER - BACKGROUND_LOGGER = BackgroundLogger(api) + if BACKGROUND_LOGGER is None: + BACKGROUND_LOGGER = BackgroundLogger(api) logger = logging.getLogger() logger.setLevel(level) logger.addHandler(TelemetryHandler()) From 25c1d9b03766f2485efc19ca7a8bedc877808044 Mon Sep 17 00:00:00 2001 From: Botao Chen Date: Tue, 14 Jan 2025 12:48:49 -0800 Subject: [PATCH 003/107] [post training] define llama stack post training dataset format (#717) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## context In this PR, we defined 2 llama stack dataset formats (instruct, dialog) - For instruct dataset format, the column schema will be [chat_completion_input, expected_answer], which is consistent with the eval data format. This dataset format is the abstract of single turn QA style post training data - For dialog dataset format, the column schema will be [dialog], which is a list of user messages and assistant messages that interleave together. During training, the whole list will be the model input and the loss is calculated on assistant messages only. This dataset format is the abstract of multi turn chat style post training data ## changes - defined the 2 llama stack dataset formats - an adapter to convert llama stack dataset format to torchtune dataset format - move dataset format validation to post training level instead of torchtune level since it's not specific to torchtune - add localfs as datasetio provider ## test instruct format - use https://huggingface.co/datasets/llamastack/evals as dataset and the training works as expected Screenshot 2025-01-09 at 5 15 14 PM - use my generated local dataset and the training works as expected Screenshot 2025-01-09 at 5 19 11 PM dialog format - use my generated local dataset and the training works as expected Screenshot 2025-01-09 at 5 23 16 PM --- llama_stack/apis/common/type_system.py | 6 ++ .../apis/post_training/post_training.py | 7 +++ .../inline/post_training/common/__init__.py | 5 ++ .../inline/post_training/common/validator.py | 52 ++++++++++++++++ .../post_training/torchtune/common/utils.py | 60 +++--------------- .../torchtune/datasets/format_adapter.py | 62 +++++++++++++++++++ .../post_training/torchtune/datasets/sft.py | 13 ++++ .../recipes/lora_finetuning_single_device.py | 18 +++--- .../utils/common/data_schema_validator.py | 1 + .../experimental-post-training/build.yaml | 3 + .../experimental-post-training/run.yaml | 30 ++++----- 11 files changed, 182 insertions(+), 75 deletions(-) create mode 100644 llama_stack/providers/inline/post_training/common/__init__.py create mode 100644 llama_stack/providers/inline/post_training/common/validator.py create mode 100644 llama_stack/providers/inline/post_training/torchtune/datasets/format_adapter.py diff --git a/llama_stack/apis/common/type_system.py b/llama_stack/apis/common/type_system.py index a653efef9..e76cfde13 100644 --- a/llama_stack/apis/common/type_system.py +++ b/llama_stack/apis/common/type_system.py @@ -54,6 +54,12 @@ class AgentTurnInputType(BaseModel): type: Literal["agent_turn_input"] = "agent_turn_input" +class DialogType(BaseModel): + # expects List[Message] for messages + # this type semantically contains the output label whereas ChatCompletionInputType does not + type: Literal["dialog"] = "dialog" + + ParamType = register_schema( Annotated[ Union[ diff --git a/llama_stack/apis/post_training/post_training.py b/llama_stack/apis/post_training/post_training.py index 8e1edbe87..8841dc1d0 100644 --- a/llama_stack/apis/post_training/post_training.py +++ b/llama_stack/apis/post_training/post_training.py @@ -27,11 +27,18 @@ class OptimizerType(Enum): sgd = "sgd" +@json_schema_type +class DatasetFormat(Enum): + instruct = "instruct" + dialog = "dialog" + + @json_schema_type class DataConfig(BaseModel): dataset_id: str batch_size: int shuffle: bool + data_format: DatasetFormat validation_dataset_id: Optional[str] = None packed: Optional[bool] = False train_on_input: Optional[bool] = False diff --git a/llama_stack/providers/inline/post_training/common/__init__.py b/llama_stack/providers/inline/post_training/common/__init__.py new file mode 100644 index 000000000..756f351d8 --- /dev/null +++ b/llama_stack/providers/inline/post_training/common/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. diff --git a/llama_stack/providers/inline/post_training/common/validator.py b/llama_stack/providers/inline/post_training/common/validator.py new file mode 100644 index 000000000..836e20c85 --- /dev/null +++ b/llama_stack/providers/inline/post_training/common/validator.py @@ -0,0 +1,52 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +# Copyright (c) Meta Platforms, IAny, nc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. +from llama_stack.apis.common.type_system import ( + ChatCompletionInputType, + DialogType, + StringType, +) +from llama_stack.apis.datasets import Datasets +from llama_stack.providers.utils.common.data_schema_validator import ( + ColumnName, + validate_dataset_schema, +) + +EXPECTED_DATASET_SCHEMA = { + "instruct": [ + { + ColumnName.chat_completion_input.value: ChatCompletionInputType(), + ColumnName.expected_answer.value: StringType(), + } + ], + "dialog": [ + { + ColumnName.dialog.value: DialogType(), + } + ], +} + + +async def validate_input_dataset_schema( + datasets_api: Datasets, + dataset_id: str, + dataset_type: str, +) -> None: + dataset_def = await datasets_api.get_dataset(dataset_id=dataset_id) + if not dataset_def.dataset_schema or len(dataset_def.dataset_schema) == 0: + raise ValueError(f"Dataset {dataset_id} does not have a schema defined.") + + if dataset_type not in EXPECTED_DATASET_SCHEMA: + raise ValueError(f"Dataset type {dataset_type} is not supported.") + + validate_dataset_schema( + dataset_def.dataset_schema, EXPECTED_DATASET_SCHEMA[dataset_type] + ) diff --git a/llama_stack/providers/inline/post_training/torchtune/common/utils.py b/llama_stack/providers/inline/post_training/torchtune/common/utils.py index b4cd43770..88011ead4 100644 --- a/llama_stack/providers/inline/post_training/torchtune/common/utils.py +++ b/llama_stack/providers/inline/post_training/torchtune/common/utils.py @@ -10,29 +10,22 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -from enum import Enum -from typing import Any, Callable, Dict, List +from typing import Any, Callable, Dict import torch from llama_models.datatypes import Model from llama_models.sku_list import resolve_model from pydantic import BaseModel +from torchtune.data._messages import InputOutputToMessages, ShareGPTToMessages from torchtune.models.llama3 import llama3_tokenizer from torchtune.models.llama3._tokenizer import Llama3Tokenizer from torchtune.models.llama3_1 import lora_llama3_1_8b from torchtune.models.llama3_2 import lora_llama3_2_3b +from torchtune.modules.transforms import Transform -from llama_stack.apis.common.type_system import ParamType, StringType -from llama_stack.apis.datasets import Datasets - - -class ColumnName(Enum): - instruction = "instruction" - input = "input" - output = "output" - text = "text" +from llama_stack.apis.post_training import DatasetFormat class ModelConfig(BaseModel): @@ -41,10 +34,6 @@ class ModelConfig(BaseModel): checkpoint_type: str -class DatasetSchema(BaseModel): - alpaca: List[Dict[str, ParamType]] - - MODEL_CONFIGS: Dict[str, ModelConfig] = { "Llama3.2-3B-Instruct": ModelConfig( model_definition=lora_llama3_2_3b, @@ -58,26 +47,11 @@ MODEL_CONFIGS: Dict[str, ModelConfig] = { ), } +DATA_FORMATS: Dict[str, Transform] = { + "instruct": InputOutputToMessages, + "dialog": ShareGPTToMessages, +} -EXPECTED_DATASET_SCHEMA = DatasetSchema( - alpaca=[ - { - ColumnName.instruction.value: StringType(), - ColumnName.input.value: StringType(), - ColumnName.output.value: StringType(), - ColumnName.text.value: StringType(), - }, - { - ColumnName.instruction.value: StringType(), - ColumnName.input.value: StringType(), - ColumnName.output.value: StringType(), - }, - { - ColumnName.instruction.value: StringType(), - ColumnName.output.value: StringType(), - }, - ] -) BuildLoraModelCallable = Callable[..., torch.nn.Module] BuildTokenizerCallable = Callable[..., Llama3Tokenizer] @@ -124,19 +98,5 @@ async def get_checkpointer_model_type( return model_config.checkpoint_type -async def validate_input_dataset_schema( - datasets_api: Datasets, - dataset_id: str, - dataset_type: str, -) -> None: - dataset_def = await datasets_api.get_dataset(dataset_id=dataset_id) - if not dataset_def.dataset_schema or len(dataset_def.dataset_schema) == 0: - raise ValueError(f"Dataset {dataset_id} does not have a schema defined.") - - if not hasattr(EXPECTED_DATASET_SCHEMA, dataset_type): - raise ValueError(f"Dataset type {dataset_type} is not supported.") - - if dataset_def.dataset_schema not in getattr(EXPECTED_DATASET_SCHEMA, dataset_type): - raise ValueError( - f"Dataset {dataset_id} does not have a correct input schema in {getattr(EXPECTED_DATASET_SCHEMA, dataset_type)}" - ) +async def get_data_transform(data_format: DatasetFormat) -> Transform: + return DATA_FORMATS[data_format.value] diff --git a/llama_stack/providers/inline/post_training/torchtune/datasets/format_adapter.py b/llama_stack/providers/inline/post_training/torchtune/datasets/format_adapter.py new file mode 100644 index 000000000..b4dfbb3c1 --- /dev/null +++ b/llama_stack/providers/inline/post_training/torchtune/datasets/format_adapter.py @@ -0,0 +1,62 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +from typing import Any, Mapping + +from llama_stack.providers.utils.common.data_schema_validator import ColumnName + + +def llama_stack_instruct_to_torchtune_instruct( + sample: Mapping[str, Any] +) -> Mapping[str, Any]: + assert ( + ColumnName.chat_completion_input.value in sample + and ColumnName.expected_answer.value in sample + ), "Invalid input row" + input_messages = eval(str(sample[ColumnName.chat_completion_input.value])) + + assert ( + len(input_messages) == 1 + ), "llama stack intruct dataset format only supports 1 user message" + input_message = input_messages[0] + + assert "content" in input_message, "content not found in input message" + input = input_message["content"] + output = sample[ColumnName.expected_answer.value] + + return { + "input": input, + "output": output, + } + + +def llama_stack_chat_to_torchtune_chat(sample: Mapping[str, Any]) -> Mapping[str, Any]: + assert ColumnName.dialog.value in sample, "Invalid input row" + role_map = {"user": "human", "assistant": "gpt"} + dialog = eval(str(sample[ColumnName.dialog.value])) + + assert len(dialog) > 1, "dialog must have at least 2 messagse" + roles = [] + conversations = [] + for message in dialog: + assert ( + "role" in message and "content" in message + ), "role and content must in message" + roles.append(message["role"]) + conversations.append( + {"from": role_map[message["role"]], "value": message["content"]} + ) + + assert roles[0] == "user", "first message must be from user" + assert "assistant" in roles, "at least 1 message should be from assistant" + + return {"conversations": conversations} diff --git a/llama_stack/providers/inline/post_training/torchtune/datasets/sft.py b/llama_stack/providers/inline/post_training/torchtune/datasets/sft.py index 1f91dc73f..1a5aade09 100644 --- a/llama_stack/providers/inline/post_training/torchtune/datasets/sft.py +++ b/llama_stack/providers/inline/post_training/torchtune/datasets/sft.py @@ -19,6 +19,11 @@ from torchtune.data._common import CROSS_ENTROPY_IGNORE_IDX from torchtune.data._messages import validate_messages from torchtune.modules.transforms import Transform +from llama_stack.providers.inline.post_training.torchtune.datasets.format_adapter import ( + llama_stack_chat_to_torchtune_chat, + llama_stack_instruct_to_torchtune_instruct, +) + class SFTDataset(Dataset): def __init__( @@ -26,10 +31,12 @@ class SFTDataset(Dataset): rows: List[Dict[str, Any]], message_transform: Transform, model_transform: Transform, + dataset_type: str, ) -> None: self._rows = rows self._message_transform = message_transform self._model_transform = model_transform + self._dataset_type = dataset_type def __len__(self): return len(self._rows) @@ -39,6 +46,12 @@ class SFTDataset(Dataset): return self._prepare_sample(sample) def _prepare_sample(self, sample: Mapping[str, Any]) -> Dict[str, Any]: + if self._dataset_type == "instruct": + sample = llama_stack_instruct_to_torchtune_instruct(sample) + elif self._dataset_type == "dialog": + sample = llama_stack_chat_to_torchtune_chat(sample) + else: + raise ValueError(f"Invalid dataset type: {self._dataset_type}") transformed_sample = self._message_transform(sample) if "messages" in transformed_sample: validate_messages(transformed_sample["messages"]) diff --git a/llama_stack/providers/inline/post_training/torchtune/recipes/lora_finetuning_single_device.py b/llama_stack/providers/inline/post_training/torchtune/recipes/lora_finetuning_single_device.py index 6c795d310..7543b1f4e 100644 --- a/llama_stack/providers/inline/post_training/torchtune/recipes/lora_finetuning_single_device.py +++ b/llama_stack/providers/inline/post_training/torchtune/recipes/lora_finetuning_single_device.py @@ -18,7 +18,7 @@ from torch import nn from torch.optim import Optimizer from torch.utils.data import DataLoader, DistributedSampler from torchtune import modules, training, utils as torchtune_utils -from torchtune.data import AlpacaToMessages, padded_collate_sft +from torchtune.data import padded_collate_sft from torchtune.modules.loss import CEWithChunkedOutputLoss from torchtune.modules.peft import ( @@ -47,6 +47,9 @@ from llama_stack.apis.post_training import ( from llama_stack.distribution.utils.config_dirs import DEFAULT_CHECKPOINT_DIR from llama_stack.distribution.utils.model_utils import model_local_dir +from llama_stack.providers.inline.post_training.common.validator import ( + validate_input_dataset_schema, +) from llama_stack.providers.inline.post_training.torchtune.common import utils from llama_stack.providers.inline.post_training.torchtune.common.checkpointer import ( @@ -129,8 +132,10 @@ class LoraFinetuningSingleDevice: self.seed = training.set_seed(seed=config.torch_seed) self.epochs_run = 0 self.total_epochs = training_config.n_epochs + self._data_format = training_config.data_config.data_format self._shuffle = training_config.data_config.shuffle self._batch_size = training_config.data_config.batch_size + self._train_on_input = training_config.data_config.train_on_input # this is important for debugging purpose self.max_steps_per_epoch = training_config.max_steps_per_epoch @@ -354,18 +359,17 @@ class LoraFinetuningSingleDevice: all_rows = await fetch_rows(dataset_id) rows = all_rows.rows - # Curretly only support alpaca instruct dataset - # TODO @SLR722 make the message_transform swappable and support more dataset types - # TODO @SLR722 make the input dataset schema more flexible by exposing column_map - await utils.validate_input_dataset_schema( + await validate_input_dataset_schema( datasets_api=self.datasets_api, dataset_id=dataset_id, - dataset_type="alpaca", + dataset_type=self._data_format.value, ) + data_transform = await utils.get_data_transform(self._data_format) ds = SFTDataset( rows, - message_transform=AlpacaToMessages(train_on_input=False), + message_transform=data_transform(train_on_input=self._train_on_input), model_transform=tokenizer, + dataset_type=self._data_format.value, ) sampler = DistributedSampler( diff --git a/llama_stack/providers/utils/common/data_schema_validator.py b/llama_stack/providers/utils/common/data_schema_validator.py index af58a4592..55f1078a4 100644 --- a/llama_stack/providers/utils/common/data_schema_validator.py +++ b/llama_stack/providers/utils/common/data_schema_validator.py @@ -23,6 +23,7 @@ class ColumnName(Enum): completion_input = "completion_input" generated_answer = "generated_answer" context = "context" + dialog = "dialog" VALID_SCHEMAS_FOR_SCORING = [ diff --git a/llama_stack/templates/experimental-post-training/build.yaml b/llama_stack/templates/experimental-post-training/build.yaml index aa7695bca..e04868199 100644 --- a/llama_stack/templates/experimental-post-training/build.yaml +++ b/llama_stack/templates/experimental-post-training/build.yaml @@ -13,6 +13,7 @@ distribution_spec: post_training: - inline::torchtune datasetio: + - inline::localfs - remote::huggingface telemetry: - inline::meta-reference @@ -22,4 +23,6 @@ distribution_spec: - inline::llama-guard memory: - inline::faiss + tool_runtime: + - remote::brave-search image_type: conda diff --git a/llama_stack/templates/experimental-post-training/run.yaml b/llama_stack/templates/experimental-post-training/run.yaml index a654c375e..4a7bb5c47 100644 --- a/llama_stack/templates/experimental-post-training/run.yaml +++ b/llama_stack/templates/experimental-post-training/run.yaml @@ -12,6 +12,7 @@ apis: - scoring - telemetry - post_training +- tool_runtime providers: inference: - provider_id: meta-reference-inference @@ -32,6 +33,9 @@ providers: - provider_id: huggingface-0 provider_type: remote::huggingface config: {} + - provider_id: localfs + provider_type: inline::localfs + config: {} telemetry: - provider_id: meta-reference provider_type: inline::meta-reference @@ -60,6 +64,13 @@ providers: type: sqlite namespace: null db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/meta-reference-gpu}/faiss_store.db + tool_runtime: + - provider_id: brave-search + provider_type: remote::brave-search + config: + api_key: ${env.BRAVE_SEARCH_API_KEY:} + max_results: 3 + metadata_store: namespace: null @@ -68,23 +79,6 @@ metadata_store: models: [] shields: [] memory_banks: [] -datasets: - - dataset_id: alpaca - provider_id: huggingface-0 - url: - uri: https://huggingface.co/datasets/tatsu-lab/alpaca - metadata: - path: tatsu-lab/alpaca - name: - split: train - dataset_schema: - instruction: - type: string - input: - type: string - output: - type: string - text: - type: string +datasets: [] scoring_fns: [] eval_tasks: [] From e6e4f0858c0c27976d7aed22f797ce2617a78348 Mon Sep 17 00:00:00 2001 From: Botao Chen Date: Tue, 14 Jan 2025 13:42:59 -0800 Subject: [PATCH 004/107] add braintrust to experimental-post-training template (#763) as title, add braintrust to experimental-post-training template to run llm as judge based eval for a finetuned model --- llama_stack/templates/experimental-post-training/build.yaml | 1 + llama_stack/templates/experimental-post-training/run.yaml | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/llama_stack/templates/experimental-post-training/build.yaml b/llama_stack/templates/experimental-post-training/build.yaml index e04868199..4997ab8a3 100644 --- a/llama_stack/templates/experimental-post-training/build.yaml +++ b/llama_stack/templates/experimental-post-training/build.yaml @@ -10,6 +10,7 @@ distribution_spec: - inline::meta-reference scoring: - inline::basic + - inline::braintrust post_training: - inline::torchtune datasetio: diff --git a/llama_stack/templates/experimental-post-training/run.yaml b/llama_stack/templates/experimental-post-training/run.yaml index 4a7bb5c47..2e0ee029b 100644 --- a/llama_stack/templates/experimental-post-training/run.yaml +++ b/llama_stack/templates/experimental-post-training/run.yaml @@ -29,6 +29,10 @@ providers: - provider_id: basic provider_type: inline::basic config: {} + - provider_id: braintrust + provider_type: inline::braintrust + config: + openai_api_key: ${env.OPENAI_API_KEY:} datasetio: - provider_id: huggingface-0 provider_type: remote::huggingface From 91907b714e825a1bfbca5271e0f403aab5f10752 Mon Sep 17 00:00:00 2001 From: Jeff Tang Date: Tue, 14 Jan 2025 13:45:42 -0800 Subject: [PATCH 005/107] added support of PYPI_VERSION in stack build (#762) # What does this PR do? To build a conda env for specific Llama Stack version, e.g. `PYPI_VERSION=0.0.58 llama stack build --template together --image-type conda` will install these in the llamastack-together env: ``` llama_models 0.0.58 llama_stack 0.0.58 llama_stack_client 0.0.58 ``` Without `PYPI_VERSION=`, `llama stack build --template together --image-type conda` installs the latest all. In short, provide a summary of what this PR does and why. Usually, the relevant context should be present in a linked issue. - [ ] Addresses issue (#issue) ## Test Plan Please describe: - tests you ran to verify your changes with result summaries. - provide instructions so it can be reproduced. ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- llama_stack/distribution/build_conda_env.sh | 8 +++++++- llama_stack/distribution/build_container.sh | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/llama_stack/distribution/build_conda_env.sh b/llama_stack/distribution/build_conda_env.sh index fc1e48665..461f27baa 100755 --- a/llama_stack/distribution/build_conda_env.sh +++ b/llama_stack/distribution/build_conda_env.sh @@ -105,7 +105,13 @@ ensure_conda_env_python310() { printf "Installing from LLAMA_STACK_DIR: $LLAMA_STACK_DIR\n" $CONDA_PREFIX/bin/pip install --no-cache-dir -e "$LLAMA_STACK_DIR" else - $CONDA_PREFIX/bin/pip install --no-cache-dir llama-stack + PYPI_VERSION="${PYPI_VERSION:-}" + if [ -n "$PYPI_VERSION" ]; then + SPEC_VERSION="llama-stack==${PYPI_VERSION} llama-models==${PYPI_VERSION} llama-stack-client==${PYPI_VERSION}" + else + SPEC_VERSION="llama-stack" + fi + $CONDA_PREFIX/bin/pip install --no-cache-dir $SPEC_VERSION fi if [ -n "$LLAMA_MODELS_DIR" ]; then diff --git a/llama_stack/distribution/build_container.sh b/llama_stack/distribution/build_container.sh index 286ade992..06cb19c32 100755 --- a/llama_stack/distribution/build_container.sh +++ b/llama_stack/distribution/build_container.sh @@ -9,6 +9,7 @@ LLAMA_MODELS_DIR=${LLAMA_MODELS_DIR:-} LLAMA_STACK_DIR=${LLAMA_STACK_DIR:-} TEST_PYPI_VERSION=${TEST_PYPI_VERSION:-} +PYPI_VERSION=${PYPI_VERSION:-} BUILD_PLATFORM=${BUILD_PLATFORM:-} if [ "$#" -lt 4 ]; then @@ -113,7 +114,12 @@ RUN pip install --no-cache --extra-index-url https://test.pypi.org/simple/ \ llama-models==$TEST_PYPI_VERSION llama-stack-client==$TEST_PYPI_VERSION llama-stack==$TEST_PYPI_VERSION EOF else - add_to_docker "RUN pip install --no-cache llama-stack" + if [ -n "$PYPI_VERSION" ]; then + SPEC_VERSION="llama-stack==${PYPI_VERSION} llama-models==${PYPI_VERSION} llama-stack-client==${PYPI_VERSION}" + else + SPEC_VERSION="llama-stack" + fi + add_to_docker "RUN pip install --no-cache $SPEC_VERSION" fi fi From 472feea8d4c0c917031f754e22b95092141f752c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladimir=20Ivi=C4=87?= Date: Tue, 14 Jan 2025 14:33:15 -0800 Subject: [PATCH 006/107] Fix broken tests in test_registry (#707) Summary: Tests were broken after registry.get return type was changed from `List[RoutableObjectWithProvider]` to `Optional[RoutableObjectWithProvider]` in https://github.com/meta-llama/llama-stack/commit/efe791bab7f6dedb89707e500639c4355bc36942#diff-5de152bae521b7baef01048a4c0142484f8f1c978a04f3b55f4e4dabc52835beL29 Test Plan: Run tests ``` pytest llama_stack/distribution/store/tests/test_registry.py -v collected 6 items llama_stack/distribution/store/tests/test_registry.py::test_registry_initialization PASSED [ 16%] llama_stack/distribution/store/tests/test_registry.py::test_basic_registration PASSED [ 33%] llama_stack/distribution/store/tests/test_registry.py::test_cached_registry_initialization PASSED [ 50%] llama_stack/distribution/store/tests/test_registry.py::test_cached_registry_updates PASSED [ 66%] llama_stack/distribution/store/tests/test_registry.py::test_duplicate_provider_registration PASSED [ 83%] llama_stack/distribution/store/tests/test_registry.py::test_get_all_objects PASSED [100%] ``` --- .../distribution/store/tests/test_registry.py | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/llama_stack/distribution/store/tests/test_registry.py b/llama_stack/distribution/store/tests/test_registry.py index 54bc04f9c..9c5b72f93 100644 --- a/llama_stack/distribution/store/tests/test_registry.py +++ b/llama_stack/distribution/store/tests/test_registry.py @@ -15,7 +15,8 @@ from llama_stack.distribution.store.registry import ( CachedDiskDistributionRegistry, DiskDistributionRegistry, ) -from llama_stack.providers.utils.kvstore import kvstore_impl, SqliteKVStoreConfig +from llama_stack.providers.utils.kvstore import kvstore_impl +from llama_stack.providers.utils.kvstore.config import SqliteKVStoreConfig @pytest.fixture @@ -26,14 +27,14 @@ def config(): return config -@pytest_asyncio.fixture +@pytest_asyncio.fixture(scope="function") async def registry(config): registry = DiskDistributionRegistry(await kvstore_impl(config)) await registry.initialize() return registry -@pytest_asyncio.fixture +@pytest_asyncio.fixture(scope="function") async def cached_registry(config): registry = CachedDiskDistributionRegistry(await kvstore_impl(config)) await registry.initialize() @@ -64,8 +65,8 @@ def sample_model(): @pytest.mark.asyncio async def test_registry_initialization(registry): # Test empty registry - results = await registry.get("nonexistent", "nonexistent") - assert len(results) == 0 + result = await registry.get("nonexistent", "nonexistent") + assert result is None @pytest.mark.asyncio @@ -75,18 +76,16 @@ async def test_basic_registration(registry, sample_bank, sample_model): print(f"Registering {sample_model}") await registry.register(sample_model) print("Getting bank") - results = await registry.get("memory_bank", "test_bank") - assert len(results) == 1 - result_bank = results[0] + result_bank = await registry.get("memory_bank", "test_bank") + assert result_bank is not None assert result_bank.identifier == sample_bank.identifier assert result_bank.embedding_model == sample_bank.embedding_model assert result_bank.chunk_size_in_tokens == sample_bank.chunk_size_in_tokens assert result_bank.overlap_size_in_tokens == sample_bank.overlap_size_in_tokens assert result_bank.provider_id == sample_bank.provider_id - results = await registry.get("model", "test_model") - assert len(results) == 1 - result_model = results[0] + result_model = await registry.get("model", "test_model") + assert result_model is not None assert result_model.identifier == sample_model.identifier assert result_model.provider_id == sample_model.provider_id @@ -103,9 +102,8 @@ async def test_cached_registry_initialization(config, sample_bank, sample_model) cached_registry = CachedDiskDistributionRegistry(await kvstore_impl(config)) await cached_registry.initialize() - results = await cached_registry.get("memory_bank", "test_bank") - assert len(results) == 1 - result_bank = results[0] + result_bank = await cached_registry.get("memory_bank", "test_bank") + assert result_bank is not None assert result_bank.identifier == sample_bank.identifier assert result_bank.embedding_model == sample_bank.embedding_model assert result_bank.chunk_size_in_tokens == sample_bank.chunk_size_in_tokens @@ -129,18 +127,16 @@ async def test_cached_registry_updates(config): await cached_registry.register(new_bank) # Verify in cache - results = await cached_registry.get("memory_bank", "test_bank_2") - assert len(results) == 1 - result_bank = results[0] + result_bank = await cached_registry.get("memory_bank", "test_bank_2") + assert result_bank is not None assert result_bank.identifier == new_bank.identifier assert result_bank.provider_id == new_bank.provider_id # Verify persisted to disk new_registry = DiskDistributionRegistry(await kvstore_impl(config)) await new_registry.initialize() - results = await new_registry.get("memory_bank", "test_bank_2") - assert len(results) == 1 - result_bank = results[0] + result_bank = await new_registry.get("memory_bank", "test_bank_2") + assert result_bank is not None assert result_bank.identifier == new_bank.identifier assert result_bank.provider_id == new_bank.provider_id @@ -170,10 +166,10 @@ async def test_duplicate_provider_registration(config): ) await cached_registry.register(duplicate_bank) - results = await cached_registry.get("memory_bank", "test_bank_2") - assert len(results) == 1 # Still only one result + result = await cached_registry.get("memory_bank", "test_bank_2") + assert result is not None assert ( - results[0].embedding_model == original_bank.embedding_model + result.embedding_model == original_bank.embedding_model ) # Original values preserved From 89e3f815206cc1272a25a6a4364267bb5b6cb525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladimir=20Ivi=C4=87?= Date: Tue, 14 Jan 2025 15:28:55 -0800 Subject: [PATCH 007/107] Fix fireworks run-with-safety template (#766) Summary: Fixing issue reported in https://github.com/meta-llama/llama-stack/pull/755/files#r1915696188 Test Plan: Re-run the config gen ``` pip install . python3 llama_stack/scripts/distro_codegen.py ``` --- llama_stack/templates/fireworks/fireworks.py | 22 ++++++++-------- .../templates/fireworks/run-with-safety.yaml | 25 +++++++++++-------- llama_stack/templates/fireworks/run.yaml | 2 +- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/llama_stack/templates/fireworks/fireworks.py b/llama_stack/templates/fireworks/fireworks.py index 5af4b08cc..8add75f7d 100644 --- a/llama_stack/templates/fireworks/fireworks.py +++ b/llama_stack/templates/fireworks/fireworks.py @@ -71,14 +71,6 @@ def get_distribution_template() -> DistributionTemplate: ) for m in MODEL_ALIASES ] - inference_model = ModelInput( - model_id="${env.INFERENCE_MODEL}", - provider_id="fireworks", - ) - safety_model = ModelInput( - model_id="${env.SAFETY_MODEL}", - provider_id="fireworks", - ) embedding_model = ModelInput( model_id="all-MiniLM-L6-v2", provider_id="sentence-transformers", @@ -133,6 +125,11 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::llama-guard", config={}, ), + Provider( + provider_id="llama-guard-vision", + provider_type="inline::llama-guard", + config={}, + ), Provider( provider_id="code-scanner", provider_type="inline::code-scanner", @@ -141,15 +138,18 @@ def get_distribution_template() -> DistributionTemplate: ], }, default_models=[ - inference_model, - safety_model, + *default_models, embedding_model, ], default_shields=[ ShieldInput( - shield_id="${env.SAFETY_MODEL}", + shield_id="meta-llama/Llama-Guard-3-8B", provider_id="llama-guard", ), + ShieldInput( + shield_id="meta-llama/Llama-Guard-3-11B-Vision", + provider_id="llama-guard-vision", + ), ShieldInput( shield_id="CodeScanner", provider_id="code-scanner", diff --git a/llama_stack/templates/fireworks/run-with-safety.yaml b/llama_stack/templates/fireworks/run-with-safety.yaml index 58cdce85d..a279ab820 100644 --- a/llama_stack/templates/fireworks/run-with-safety.yaml +++ b/llama_stack/templates/fireworks/run-with-safety.yaml @@ -33,6 +33,9 @@ providers: - provider_id: llama-guard provider_type: inline::llama-guard config: {} + - provider_id: llama-guard-vision + provider_type: inline::llama-guard + config: {} - provider_id: code-scanner provider_type: inline::code-scanner config: {} @@ -97,52 +100,52 @@ models: - metadata: {} model_id: meta-llama/Llama-3.1-8B-Instruct provider_id: fireworks - provider_model_id: fireworks/llama-v3p1-8b-instruct + provider_model_id: accounts/fireworks/models/llama-v3p1-8b-instruct model_type: llm - metadata: {} model_id: meta-llama/Llama-3.1-70B-Instruct provider_id: fireworks - provider_model_id: fireworks/llama-v3p1-70b-instruct + provider_model_id: accounts/fireworks/models/llama-v3p1-70b-instruct model_type: llm - metadata: {} model_id: meta-llama/Llama-3.1-405B-Instruct-FP8 provider_id: fireworks - provider_model_id: fireworks/llama-v3p1-405b-instruct + provider_model_id: accounts/fireworks/models/llama-v3p1-405b-instruct model_type: llm - metadata: {} model_id: meta-llama/Llama-3.2-1B-Instruct provider_id: fireworks - provider_model_id: fireworks/llama-v3p2-1b-instruct + provider_model_id: accounts/fireworks/models/llama-v3p2-1b-instruct model_type: llm - metadata: {} model_id: meta-llama/Llama-3.2-3B-Instruct provider_id: fireworks - provider_model_id: fireworks/llama-v3p2-3b-instruct + provider_model_id: accounts/fireworks/models/llama-v3p2-3b-instruct model_type: llm - metadata: {} model_id: meta-llama/Llama-3.2-11B-Vision-Instruct provider_id: fireworks - provider_model_id: fireworks/llama-v3p2-11b-vision-instruct + provider_model_id: accounts/fireworks/models/llama-v3p2-11b-vision-instruct model_type: llm - metadata: {} model_id: meta-llama/Llama-3.2-90B-Vision-Instruct provider_id: fireworks - provider_model_id: fireworks/llama-v3p2-90b-vision-instruct + provider_model_id: accounts/fireworks/models/llama-v3p2-90b-vision-instruct model_type: llm - metadata: {} model_id: meta-llama/Llama-3.3-70B-Instruct provider_id: fireworks - provider_model_id: fireworks/llama-v3p3-70b-instruct + provider_model_id: accounts/fireworks/models/llama-v3p3-70b-instruct model_type: llm - metadata: {} model_id: meta-llama/Llama-Guard-3-8B provider_id: fireworks - provider_model_id: fireworks/llama-guard-3-8b + provider_model_id: accounts/fireworks/models/llama-guard-3-8b model_type: llm - metadata: {} model_id: meta-llama/Llama-Guard-3-11B-Vision provider_id: fireworks - provider_model_id: fireworks/llama-guard-3-11b-vision + provider_model_id: accounts/fireworks/models/llama-guard-3-11b-vision model_type: llm - metadata: embedding_dimension: 384 @@ -152,6 +155,8 @@ models: shields: - shield_id: meta-llama/Llama-Guard-3-8B provider_id: llama-guard +- shield_id: meta-llama/Llama-Guard-3-11B-Vision + provider_id: llama-guard-vision - shield_id: CodeScanner provider_id: code-scanner memory_banks: [] diff --git a/llama_stack/templates/fireworks/run.yaml b/llama_stack/templates/fireworks/run.yaml index 6c41b3ed7..79fafe66c 100644 --- a/llama_stack/templates/fireworks/run.yaml +++ b/llama_stack/templates/fireworks/run.yaml @@ -47,7 +47,7 @@ providers: config: service_name: ${env.OTEL_SERVICE_NAME:llama-stack} sinks: ${env.TELEMETRY_SINKS:console,sqlite} - sqlite_db_path: ${env.SQLITE_DB_PATH:~/.llama/distributions/accounts/fireworks/models/trace_store.db} + sqlite_db_path: ${env.SQLITE_DB_PATH:~/.llama/distributions/fireworks/trace_store.db} eval: - provider_id: meta-reference provider_type: inline::meta-reference From b2b82d4a9035ec40348505ca65b1d05381119939 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Tue, 14 Jan 2025 17:54:22 -0800 Subject: [PATCH 008/107] removing unused script file --- .../distribution/tests/library_client_test.py | 129 ------------------ 1 file changed, 129 deletions(-) delete mode 100644 llama_stack/distribution/tests/library_client_test.py diff --git a/llama_stack/distribution/tests/library_client_test.py b/llama_stack/distribution/tests/library_client_test.py deleted file mode 100644 index a919ab223..000000000 --- a/llama_stack/distribution/tests/library_client_test.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the terms described in the LICENSE file in -# the root directory of this source tree. - -import argparse -import os - -from llama_stack.distribution.library_client import LlamaStackAsLibraryClient -from llama_stack_client.lib.agents.agent import Agent -from llama_stack_client.lib.agents.event_logger import EventLogger as AgentEventLogger -from llama_stack_client.lib.inference.event_logger import EventLogger -from llama_stack_client.types import Attachment, UserMessage -from llama_stack_client.types.agent_create_params import AgentConfig - - -def main(config_path: str): - client = LlamaStackAsLibraryClient(config_path) - if not client.initialize(): - return - - models = client.models.list() - print("\nModels:") - for model in models: - print(model) - - if not models: - print("No models found, skipping chat completion test") - return - - model_id = next(m.identifier for m in models if "8b" in m.identifier.lower()) - print(f"Using model: {model_id}") - response = client.inference.chat_completion( - messages=[UserMessage(content="What is the capital of France?", role="user")], - model_id=model_id, - stream=False, - ) - print("\nChat completion response (non-stream):") - print(response) - - response = client.inference.chat_completion( - messages=[UserMessage(content="What is the capital of France?", role="user")], - model_id=model_id, - stream=True, - ) - - print("\nChat completion response (stream):") - for log in EventLogger().log(response): - log.print() - - print("\nAgent test:") - agent_config = AgentConfig( - model=model_id, - instructions="You are a helpful assistant", - sampling_params={ - "strategy": "greedy", - "temperature": 1.0, - "top_p": 0.9, - }, - tools=( - [ - { - "type": "brave_search", - "engine": "brave", - "api_key": os.getenv("BRAVE_SEARCH_API_KEY"), - } - ] - if os.getenv("BRAVE_SEARCH_API_KEY") - else [] - ) - + ( - [ - { - "type": "code_interpreter", - } - ] - ), - tool_choice="required", - input_shields=[], - output_shields=[], - enable_session_persistence=False, - ) - agent = Agent(client, agent_config) - user_prompts = [ - "Hello", - "Which players played in the winning team of the NBA western conference semifinals of 2024, please use tools", - ] - user_prompts = [ - ( - "Here is a csv, can you describe it ?", - [ - Attachment( - content="https://raw.githubusercontent.com/meta-llama/llama-stack-apps/main/examples/resources/inflation.csv", - mime_type="test/csv", - ) - ], - ), - ("Which year ended with the highest inflation ?", None), - ( - "What macro economic situations that led to such high inflation in that period?", - None, - ), - ("Plot average yearly inflation as a time series", None), - ] - - session_id = agent.create_session("test-session") - - for prompt, attachments in user_prompts: - response = agent.create_turn( - messages=[ - { - "role": "user", - "content": prompt, - } - ], - attachments=attachments, - session_id=session_id, - ) - - for log in AgentEventLogger().log(response): - log.print() - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("config_path", help="Path to the config YAML file") - args = parser.parse_args() - main(args.config_path) From 52a21ce78fb24e527b502f3aabf2672f82a68c40 Mon Sep 17 00:00:00 2001 From: Botao Chen Date: Tue, 14 Jan 2025 19:19:38 -0800 Subject: [PATCH 009/107] Free up memory after post training finishes (#770) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## context Currently, the GPU memory will be continuously occupied after the training finishes. In this PR, we explicitly delete the reference and clean up the memory after training finishes. ## test Before the change, after training a llama 3.2 3B model, >6GB GPU memory is still occupied After the change, after training a llama 3.2 3B model, the GPU memory drops to ~1GB Screenshot 2025-01-14 at 6 05 17 PM --- .../torchtune/recipes/lora_finetuning_single_device.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/llama_stack/providers/inline/post_training/torchtune/recipes/lora_finetuning_single_device.py b/llama_stack/providers/inline/post_training/torchtune/recipes/lora_finetuning_single_device.py index 7543b1f4e..80e206ebb 100644 --- a/llama_stack/providers/inline/post_training/torchtune/recipes/lora_finetuning_single_device.py +++ b/llama_stack/providers/inline/post_training/torchtune/recipes/lora_finetuning_single_device.py @@ -4,6 +4,7 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. +import gc import logging import os import time @@ -580,6 +581,12 @@ class LoraFinetuningSingleDevice: checkpoint.training_metrics = training_metrics checkpoints.append(checkpoint) + # clean up the memory after training finishes + self._model.to("cpu") + del self._model + gc.collect() + torch.cuda.empty_cache() + return (memory_stats, checkpoints) async def validation(self) -> Tuple[float, float]: From 300e6e2702900182b65931466bbd6449024ebaf4 Mon Sep 17 00:00:00 2001 From: Yuan Tang Date: Wed, 15 Jan 2025 08:34:08 -0500 Subject: [PATCH 010/107] Fix issue when generating distros (#755) Addressed comment https://github.com/meta-llama/llama-stack/pull/723#issuecomment-2581902075. cc @yanxi0830 I am not 100% sure if the diff is correct though but this is the result of running `python llama_stack/scripts/distro_codegen.py`. --------- Signed-off-by: Yuan Tang --- distributions/dependencies.json | 264 +++++++++--------- .../self_hosted_distro/cerebras.md | 10 - llama_stack/templates/remote-vllm/vllm.py | 2 +- 3 files changed, 133 insertions(+), 143 deletions(-) diff --git a/distributions/dependencies.json b/distributions/dependencies.json index bd363ea40..424815419 100644 --- a/distributions/dependencies.json +++ b/distributions/dependencies.json @@ -1,9 +1,9 @@ { - "hf-serverless": [ - "aiohttp", + "bedrock": [ "aiosqlite", "autoevals", "blobfile", + "boto3", "chardet", "chromadb-client", "datasets", @@ -11,103 +11,6 @@ "fastapi", "fire", "httpx", - "huggingface_hub", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "tqdm", - "transformers", - "uvicorn", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], - "together": [ - "aiosqlite", - "autoevals", - "blobfile", - "chardet", - "chromadb-client", - "datasets", - "faiss-cpu", - "fastapi", - "fire", - "httpx", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "together", - "tqdm", - "transformers", - "uvicorn", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], - "vllm-gpu": [ - "aiosqlite", - "autoevals", - "blobfile", - "chardet", - "chromadb-client", - "datasets", - "faiss-cpu", - "fastapi", - "fire", - "httpx", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "tqdm", - "transformers", - "uvicorn", - "vllm", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], - "remote-vllm": [ - "aiosqlite", - "blobfile", - "chardet", - "chromadb-client", - "faiss-cpu", - "fastapi", - "fire", - "httpx", "matplotlib", "nltk", "numpy", @@ -162,7 +65,7 @@ "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ], - "tgi": [ + "hf-endpoint": [ "aiohttp", "aiosqlite", "autoevals", @@ -196,11 +99,11 @@ "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ], - "bedrock": [ + "hf-serverless": [ + "aiohttp", "aiosqlite", "autoevals", "blobfile", - "boto3", "chardet", "chromadb-client", "datasets", @@ -208,6 +111,7 @@ "fastapi", "fire", "httpx", + "huggingface_hub", "matplotlib", "nltk", "numpy", @@ -309,35 +213,6 @@ "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ], - "cerebras": [ - "aiosqlite", - "blobfile", - "cerebras_cloud_sdk", - "chardet", - "faiss-cpu", - "fastapi", - "fire", - "httpx", - "matplotlib", - "nltk", - "numpy", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "tqdm", - "transformers", - "uvicorn", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], "ollama": [ "aiohttp", "aiosqlite", @@ -372,7 +247,7 @@ "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ], - "hf-endpoint": [ + "tgi": [ "aiohttp", "aiosqlite", "autoevals", @@ -405,5 +280,130 @@ "uvicorn", "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "together": [ + "aiosqlite", + "autoevals", + "blobfile", + "chardet", + "chromadb-client", + "datasets", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "together", + "tqdm", + "transformers", + "uvicorn", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "cerebras": [ + "aiosqlite", + "blobfile", + "cerebras_cloud_sdk", + "chardet", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "matplotlib", + "nltk", + "numpy", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "remote-vllm": [ + "aiosqlite", + "blobfile", + "chardet", + "chromadb-client", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "vllm-gpu": [ + "aiosqlite", + "autoevals", + "blobfile", + "chardet", + "chromadb-client", + "datasets", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", + "vllm", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" ] } diff --git a/docs/source/distributions/self_hosted_distro/cerebras.md b/docs/source/distributions/self_hosted_distro/cerebras.md index 7ebcdfb94..be69c8f92 100644 --- a/docs/source/distributions/self_hosted_distro/cerebras.md +++ b/docs/source/distributions/self_hosted_distro/cerebras.md @@ -1,15 +1,5 @@ ---- -orphan: true ---- # Cerebras Distribution -```{toctree} -:maxdepth: 2 -:hidden: - -self -``` - The `llamastack/distribution-cerebras` distribution consists of the following provider configurations. | API | Provider(s) | diff --git a/llama_stack/templates/remote-vllm/vllm.py b/llama_stack/templates/remote-vllm/vllm.py index ecaa2cf14..8693d70d3 100644 --- a/llama_stack/templates/remote-vllm/vllm.py +++ b/llama_stack/templates/remote-vllm/vllm.py @@ -134,7 +134,7 @@ def get_distribution_template() -> DistributionTemplate: "Inference model loaded into the vLLM server", ), "VLLM_URL": ( - "http://host.docker.internal:5100}/v1", + "http://host.docker.internal:5100/v1", "URL of the vLLM server with the main inference model", ), "MAX_TOKENS": ( From a51c8b4efc6700df597f6be41f99a86b36086837 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Wed, 15 Jan 2025 05:38:51 -0800 Subject: [PATCH 011/107] Convert `SamplingParams.strategy` to a union (#767) # What does this PR do? Cleans up how we provide sampling params. Earlier, strategy was an enum and all params (top_p, temperature, top_k) across all strategies were grouped. We now have a strategy union object with each strategy (greedy, top_p, top_k) having its corresponding params. Earlier, ``` class SamplingParams: strategy: enum () top_p, temperature, top_k and other params ``` However, the `strategy` field was not being used in any providers making it confusing to know the exact sampling behavior purely based on the params since you could pass temperature, top_p, top_k and how the provider would interpret those would not be clear. Hence we introduced -- a union where the strategy and relevant params are all clubbed together to avoid this confusion. Have updated all providers, tests, notebooks, readme and otehr places where sampling params was being used to use the new format. ## Test Plan `pytest llama_stack/providers/tests/inference/groq/test_groq_utils.py` // inference on ollama, fireworks and together `with-proxy pytest -v -s -k "ollama" --inference-model="meta-llama/Llama-3.1-8B-Instruct" llama_stack/providers/tests/inference/test_text_inference.py ` // agents on fireworks `pytest -v -s -k 'fireworks and create_agent' --inference-model="meta-llama/Llama-3.1-8B-Instruct" llama_stack/providers/tests/agents/test_agents.py --safety-shield="meta-llama/Llama-Guard-3-8B"` ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [X] Ran pre-commit to handle lint / formatting issues. - [X] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [X] Updated relevant documentation. - [X] Wrote necessary unit or integration tests. --------- Co-authored-by: Hardik Shah --- docs/getting_started.ipynb | 169 +++++++++++------- .../Llama_Stack_Benchmark_Evals.ipynb | 63 +++---- ...Llama_Stack_Building_AI_Applications.ipynb | 94 +++++----- docs/resources/llama-stack-spec.html | 106 ++++++++--- docs/resources/llama-stack-spec.yaml | 75 +++++--- docs/source/benchmark_evaluations/index.md | 16 +- docs/source/building_applications/index.md | 6 +- .../references/evals_reference/index.md | 30 ++-- .../references/llama_cli_reference/index.md | 1 - .../llama_stack_client_cli_reference.md | 7 +- .../04_Tool_Calling101.ipynb | 78 ++++---- docs/zero_to_hero_guide/07_Agents101.ipynb | 20 ++- docs/zero_to_hero_guide/README.md | 10 +- ..._Using_Together's_Llama_Stack_Server.ipynb | 94 +++++----- llama_stack/cli/model/describe.py | 3 +- .../ui/page/evaluations/native_eval.py | 16 +- .../distribution/ui/page/playground/chat.py | 12 +- .../distribution/ui/page/playground/rag.py | 13 +- .../inference/meta_reference/generation.py | 27 ++- .../providers/inline/inference/vllm/vllm.py | 20 +-- .../remote/inference/bedrock/bedrock.py | 18 +- .../remote/inference/cerebras/cerebras.py | 5 +- .../remote/inference/groq/groq_utils.py | 8 +- .../remote/inference/nvidia/openai_utils.py | 26 +-- .../providers/tests/agents/test_agents.py | 8 +- .../tests/inference/groq/test_groq_utils.py | 34 ++-- .../tests/inference/test_text_inference.py | 3 +- .../utils/inference/openai_compat.py | 29 ++- tests/client-sdk/agents/test_agents.py | 8 +- 29 files changed, 611 insertions(+), 388 deletions(-) diff --git a/docs/getting_started.ipynb b/docs/getting_started.ipynb index fa527f1a0..921869b33 100644 --- a/docs/getting_started.ipynb +++ b/docs/getting_started.ipynb @@ -713,13 +713,15 @@ ], "source": [ "import os\n", + "\n", "from google.colab import userdata\n", "\n", - "os.environ['TOGETHER_API_KEY'] = userdata.get('TOGETHER_API_KEY')\n", + "os.environ[\"TOGETHER_API_KEY\"] = userdata.get(\"TOGETHER_API_KEY\")\n", "\n", "from llama_stack.distribution.library_client import LlamaStackAsLibraryClient\n", + "\n", "client = LlamaStackAsLibraryClient(\"together\")\n", - "_ = client.initialize()" + "_ = client.initialize()\n" ] }, { @@ -769,6 +771,7 @@ ], "source": [ "from rich.pretty import pprint\n", + "\n", "print(\"Available models:\")\n", "for m in client.models.list():\n", " print(f\"{m.identifier} (provider's alias: {m.provider_resource_id}) \")\n", @@ -777,7 +780,7 @@ "print(\"Available shields (safety models):\")\n", "for s in client.shields.list():\n", " print(s.identifier)\n", - "print(\"----\")" + "print(\"----\")\n" ] }, { @@ -822,7 +825,7 @@ "source": [ "model_id = \"meta-llama/Llama-3.1-70B-Instruct\"\n", "\n", - "model_id" + "model_id\n" ] }, { @@ -863,11 +866,11 @@ " model_id=model_id,\n", " messages=[\n", " {\"role\": \"system\", \"content\": \"You are a friendly assistant.\"},\n", - " {\"role\": \"user\", \"content\": \"Write a two-sentence poem about llama.\"}\n", + " {\"role\": \"user\", \"content\": \"Write a two-sentence poem about llama.\"},\n", " ],\n", ")\n", "\n", - "print(response.completion_message.content)" + "print(response.completion_message.content)\n" ] }, { @@ -900,12 +903,13 @@ "source": [ "from termcolor import cprint\n", "\n", + "\n", "def chat_loop():\n", " conversation_history = []\n", " while True:\n", - " user_input = input('User> ')\n", - " if user_input.lower() in ['exit', 'quit', 'bye']:\n", - " cprint('Ending conversation. Goodbye!', 'yellow')\n", + " user_input = input(\"User> \")\n", + " if user_input.lower() in [\"exit\", \"quit\", \"bye\"]:\n", + " cprint(\"Ending conversation. Goodbye!\", \"yellow\")\n", " break\n", "\n", " user_message = {\"role\": \"user\", \"content\": user_input}\n", @@ -915,14 +919,15 @@ " messages=conversation_history,\n", " model_id=model_id,\n", " )\n", - " cprint(f'> Response: {response.completion_message.content}', 'cyan')\n", + " cprint(f\"> Response: {response.completion_message.content}\", \"cyan\")\n", "\n", " assistant_message = {\n", - " \"role\": \"assistant\", # was user\n", + " \"role\": \"assistant\", # was user\n", " \"content\": response.completion_message.content,\n", " }\n", " conversation_history.append(assistant_message)\n", "\n", + "\n", "chat_loop()\n" ] }, @@ -978,21 +983,18 @@ "source": [ "from llama_stack_client.lib.inference.event_logger import EventLogger\n", "\n", - "message = {\n", - " \"role\": \"user\",\n", - " \"content\": 'Write me a sonnet about llama'\n", - "}\n", - "print(f'User> {message[\"content\"]}', 'green')\n", + "message = {\"role\": \"user\", \"content\": \"Write me a sonnet about llama\"}\n", + "print(f'User> {message[\"content\"]}', \"green\")\n", "\n", "response = client.inference.chat_completion(\n", " messages=[message],\n", " model_id=model_id,\n", - " stream=True, # <-----------\n", + " stream=True, # <-----------\n", ")\n", "\n", "# Print the tokens while they are received\n", "for log in EventLogger().log(response):\n", - " log.print()" + " log.print()\n" ] }, { @@ -1045,26 +1047,26 @@ "source": [ "from pydantic import BaseModel\n", "\n", + "\n", "class Output(BaseModel):\n", " name: str\n", " year_born: str\n", " year_retired: str\n", "\n", + "\n", "user_input = \"Michael Jordan was born in 1963. He played basketball for the Chicago Bulls. He retired in 2003. Extract this information into JSON for me. \"\n", "response = client.inference.completion(\n", " model_id=model_id,\n", " content=user_input,\n", " stream=False,\n", - " sampling_params={\n", - " \"max_tokens\": 50,\n", - " },\n", + " sampling_params={\"strategy\": {\"type\": \"greedy\"}, \"max_tokens\": 50},\n", " response_format={\n", " \"type\": \"json_schema\",\n", " \"json_schema\": Output.model_json_schema(),\n", " },\n", ")\n", "\n", - "pprint(response)" + "pprint(response)\n" ] }, { @@ -1220,7 +1222,7 @@ " shield_id=available_shields[0],\n", " params={},\n", " )\n", - " pprint(response)" + " pprint(response)\n" ] }, { @@ -1489,8 +1491,8 @@ "source": [ "from llama_stack_client.lib.agents.agent import Agent\n", "from llama_stack_client.lib.agents.event_logger import EventLogger\n", - "from llama_stack_client.types.agent_create_params import AgentConfig\n", "from llama_stack_client.types import Attachment\n", + "from llama_stack_client.types.agent_create_params import AgentConfig\n", "from termcolor import cprint\n", "\n", "urls = [\"chat.rst\", \"llama3.rst\", \"datasets.rst\", \"lora_finetune.rst\"]\n", @@ -1522,14 +1524,14 @@ " ),\n", "]\n", "for prompt, attachments in user_prompts:\n", - " cprint(f'User> {prompt}', 'green')\n", + " cprint(f\"User> {prompt}\", \"green\")\n", " response = rag_agent.create_turn(\n", " messages=[{\"role\": \"user\", \"content\": prompt}],\n", " attachments=attachments,\n", " session_id=session_id,\n", " )\n", " for log in EventLogger().log(response):\n", - " log.print()" + " log.print()\n" ] }, { @@ -1560,8 +1562,8 @@ "search_tool = {\n", " \"type\": \"brave_search\",\n", " \"engine\": \"tavily\",\n", - " \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\")\n", - "}" + " \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\"),\n", + "}\n" ] }, { @@ -1608,7 +1610,7 @@ "\n", "session_id = agent.create_session(\"test-session\")\n", "for prompt in user_prompts:\n", - " cprint(f'User> {prompt}', 'green')\n", + " cprint(f\"User> {prompt}\", \"green\")\n", " response = agent.create_turn(\n", " messages=[\n", " {\n", @@ -1758,7 +1760,7 @@ " search_tool,\n", " {\n", " \"type\": \"code_interpreter\",\n", - " }\n", + " },\n", " ],\n", " tool_choice=\"required\",\n", " input_shields=[],\n", @@ -1788,7 +1790,7 @@ "]\n", "\n", "for prompt in user_prompts:\n", - " cprint(f'User> {prompt}', 'green')\n", + " cprint(f\"User> {prompt}\", \"green\")\n", " response = codex_agent.create_turn(\n", " messages=[\n", " {\n", @@ -1841,27 +1843,57 @@ } ], "source": [ - "import pandas as pd\n", "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", "\n", "# Read the CSV file\n", - "df = pd.read_csv('/tmp/tmpco0s0o4_/LOdZoVp1inflation.csv')\n", + "df = pd.read_csv(\"/tmp/tmpco0s0o4_/LOdZoVp1inflation.csv\")\n", "\n", "# Extract the year and inflation rate from the CSV file\n", - "df['Year'] = pd.to_datetime(df['Year'], format='%Y')\n", - "df = df.rename(columns={'Jan': 'Jan Rate', 'Feb': 'Feb Rate', 'Mar': 'Mar Rate', 'Apr': 'Apr Rate', 'May': 'May Rate', 'Jun': 'Jun Rate', 'Jul': 'Jul Rate', 'Aug': 'Aug Rate', 'Sep': 'Sep Rate', 'Oct': 'Oct Rate', 'Nov': 'Nov Rate', 'Dec': 'Dec Rate'})\n", + "df[\"Year\"] = pd.to_datetime(df[\"Year\"], format=\"%Y\")\n", + "df = df.rename(\n", + " columns={\n", + " \"Jan\": \"Jan Rate\",\n", + " \"Feb\": \"Feb Rate\",\n", + " \"Mar\": \"Mar Rate\",\n", + " \"Apr\": \"Apr Rate\",\n", + " \"May\": \"May Rate\",\n", + " \"Jun\": \"Jun Rate\",\n", + " \"Jul\": \"Jul Rate\",\n", + " \"Aug\": \"Aug Rate\",\n", + " \"Sep\": \"Sep Rate\",\n", + " \"Oct\": \"Oct Rate\",\n", + " \"Nov\": \"Nov Rate\",\n", + " \"Dec\": \"Dec Rate\",\n", + " }\n", + ")\n", "\n", "# Calculate the average yearly inflation rate\n", - "df['Yearly Inflation'] = df[['Jan Rate', 'Feb Rate', 'Mar Rate', 'Apr Rate', 'May Rate', 'Jun Rate', 'Jul Rate', 'Aug Rate', 'Sep Rate', 'Oct Rate', 'Nov Rate', 'Dec Rate']].mean(axis=1)\n", + "df[\"Yearly Inflation\"] = df[\n", + " [\n", + " \"Jan Rate\",\n", + " \"Feb Rate\",\n", + " \"Mar Rate\",\n", + " \"Apr Rate\",\n", + " \"May Rate\",\n", + " \"Jun Rate\",\n", + " \"Jul Rate\",\n", + " \"Aug Rate\",\n", + " \"Sep Rate\",\n", + " \"Oct Rate\",\n", + " \"Nov Rate\",\n", + " \"Dec Rate\",\n", + " ]\n", + "].mean(axis=1)\n", "\n", "# Plot the average yearly inflation rate as a time series\n", "plt.figure(figsize=(10, 6))\n", - "plt.plot(df['Year'], df['Yearly Inflation'], marker='o')\n", - "plt.title('Average Yearly Inflation Rate')\n", - "plt.xlabel('Year')\n", - "plt.ylabel('Inflation Rate (%)')\n", + "plt.plot(df[\"Year\"], df[\"Yearly Inflation\"], marker=\"o\")\n", + "plt.title(\"Average Yearly Inflation Rate\")\n", + "plt.xlabel(\"Year\")\n", + "plt.ylabel(\"Inflation Rate (%)\")\n", "plt.grid(True)\n", - "plt.show()" + "plt.show()\n" ] }, { @@ -2035,6 +2067,8 @@ "source": [ "# disable logging for clean server logs\n", "import logging\n", + "\n", + "\n", "def remove_root_handlers():\n", " root_logger = logging.getLogger()\n", " for handler in root_logger.handlers[:]:\n", @@ -2042,7 +2076,7 @@ " print(f\"Removed handler {handler.__class__.__name__} from root logger\")\n", "\n", "\n", - "remove_root_handlers()" + "remove_root_handlers()\n" ] }, { @@ -2083,10 +2117,10 @@ } ], "source": [ + "from google.colab import userdata\n", "from llama_stack_client.lib.agents.agent import Agent\n", "from llama_stack_client.lib.agents.event_logger import EventLogger\n", "from llama_stack_client.types.agent_create_params import AgentConfig\n", - "from google.colab import userdata\n", "\n", "agent_config = AgentConfig(\n", " model=\"meta-llama/Llama-3.1-405B-Instruct\",\n", @@ -2096,7 +2130,7 @@ " {\n", " \"type\": \"brave_search\",\n", " \"engine\": \"tavily\",\n", - " \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\")\n", + " \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\"),\n", " }\n", " ]\n", " ),\n", @@ -2125,7 +2159,7 @@ " )\n", "\n", " for log in EventLogger().log(response):\n", - " log.print()" + " log.print()\n" ] }, { @@ -2265,20 +2299,21 @@ "source": [ "print(f\"Getting traces for session_id={session_id}\")\n", "import json\n", + "\n", "from rich.pretty import pprint\n", "\n", "agent_logs = []\n", "\n", "for span in client.telemetry.query_spans(\n", " attribute_filters=[\n", - " {\"key\": \"session_id\", \"op\": \"eq\", \"value\": session_id},\n", + " {\"key\": \"session_id\", \"op\": \"eq\", \"value\": session_id},\n", " ],\n", - " attributes_to_return=[\"input\", \"output\"]\n", - " ):\n", - " if span.attributes[\"output\"] != \"no shields\":\n", - " agent_logs.append(span.attributes)\n", + " attributes_to_return=[\"input\", \"output\"],\n", + "):\n", + " if span.attributes[\"output\"] != \"no shields\":\n", + " agent_logs.append(span.attributes)\n", "\n", - "pprint(agent_logs)" + "pprint(agent_logs)\n" ] }, { @@ -2389,23 +2424,25 @@ "eval_rows = []\n", "\n", "for log in agent_logs:\n", - " last_msg = log['input'][-1]\n", - " if \"\\\"role\\\":\\\"user\\\"\" in last_msg:\n", - " eval_rows.append(\n", - " {\n", - " \"input_query\": last_msg,\n", - " \"generated_answer\": log[\"output\"],\n", - " # check if generated_answer uses tools brave_search\n", - " \"expected_answer\": \"brave_search\",\n", - " },\n", - " )\n", + " last_msg = log[\"input\"][-1]\n", + " if '\"role\":\"user\"' in last_msg:\n", + " eval_rows.append(\n", + " {\n", + " \"input_query\": last_msg,\n", + " \"generated_answer\": log[\"output\"],\n", + " # check if generated_answer uses tools brave_search\n", + " \"expected_answer\": \"brave_search\",\n", + " },\n", + " )\n", "\n", "pprint(eval_rows)\n", "scoring_params = {\n", " \"basic::subset_of\": None,\n", "}\n", - "scoring_response = client.scoring.score(input_rows=eval_rows, scoring_functions=scoring_params)\n", - "pprint(scoring_response)" + "scoring_response = client.scoring.score(\n", + " input_rows=eval_rows, scoring_functions=scoring_params\n", + ")\n", + "pprint(scoring_response)\n" ] }, { @@ -2506,7 +2543,9 @@ "EXPECTED_RESPONSE: {expected_answer}\n", "\"\"\"\n", "\n", - "input_query = \"What are the top 5 topics that were explained? Only list succinct bullet points.\"\n", + "input_query = (\n", + " \"What are the top 5 topics that were explained? Only list succinct bullet points.\"\n", + ")\n", "generated_answer = \"\"\"\n", "Here are the top 5 topics that were explained in the documentation for Torchtune:\n", "\n", @@ -2537,7 +2576,7 @@ "}\n", "\n", "response = client.scoring.score(input_rows=rows, scoring_functions=scoring_params)\n", - "pprint(response)" + "pprint(response)\n" ] }, { diff --git a/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb b/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb index 4810425d2..83891b7ac 100644 --- a/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb +++ b/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb @@ -618,11 +618,13 @@ ], "source": [ "import os\n", + "\n", "from google.colab import userdata\n", "\n", - "os.environ['TOGETHER_API_KEY'] = userdata.get('TOGETHER_API_KEY')\n", + "os.environ[\"TOGETHER_API_KEY\"] = userdata.get(\"TOGETHER_API_KEY\")\n", "\n", "from llama_stack.distribution.library_client import LlamaStackAsLibraryClient\n", + "\n", "client = LlamaStackAsLibraryClient(\"together\")\n", "_ = client.initialize()\n", "\n", @@ -631,7 +633,7 @@ " model_id=\"meta-llama/Llama-3.1-405B-Instruct\",\n", " provider_model_id=\"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\",\n", " provider_id=\"together\",\n", - ")" + ")\n" ] }, { @@ -668,7 +670,7 @@ "source": [ "name = \"llamastack/mmmu\"\n", "subset = \"Agriculture\"\n", - "split = \"dev\"" + "split = \"dev\"\n" ] }, { @@ -914,9 +916,10 @@ ], "source": [ "import datasets\n", + "\n", "ds = datasets.load_dataset(path=name, name=subset, split=split)\n", "ds = ds.select_columns([\"chat_completion_input\", \"input_query\", \"expected_answer\"])\n", - "eval_rows = ds.to_pandas().to_dict(orient=\"records\")" + "eval_rows = ds.to_pandas().to_dict(orient=\"records\")\n" ] }, { @@ -1014,8 +1017,8 @@ } ], "source": [ - "from tqdm import tqdm\n", "from rich.pretty import pprint\n", + "from tqdm import tqdm\n", "\n", "SYSTEM_PROMPT_TEMPLATE = \"\"\"\n", "You are an expert in {subject} whose job is to answer questions from the user using images.\n", @@ -1039,7 +1042,7 @@ "client.eval_tasks.register(\n", " eval_task_id=\"meta-reference::mmmu\",\n", " dataset_id=f\"mmmu-{subset}-{split}\",\n", - " scoring_functions=[\"basic::regex_parser_multiple_choice_answer\"]\n", + " scoring_functions=[\"basic::regex_parser_multiple_choice_answer\"],\n", ")\n", "\n", "response = client.eval.evaluate_rows(\n", @@ -1052,16 +1055,17 @@ " \"type\": \"model\",\n", " \"model\": \"meta-llama/Llama-3.2-90B-Vision-Instruct\",\n", " \"sampling_params\": {\n", - " \"temperature\": 0.0,\n", + " \"strategy\": {\n", + " \"type\": \"greedy\",\n", + " },\n", " \"max_tokens\": 4096,\n", - " \"top_p\": 0.9,\n", " \"repeat_penalty\": 1.0,\n", " },\n", - " \"system_message\": system_message\n", - " }\n", - " }\n", + " \"system_message\": system_message,\n", + " },\n", + " },\n", ")\n", - "pprint(response)" + "pprint(response)\n" ] }, { @@ -1098,8 +1102,8 @@ " \"input_query\": {\"type\": \"string\"},\n", " \"expected_answer\": {\"type\": \"string\"},\n", " \"chat_completion_input\": {\"type\": \"chat_completion_input\"},\n", - " }\n", - ")" + " },\n", + ")\n" ] }, { @@ -1113,7 +1117,7 @@ "eval_rows = client.datasetio.get_rows_paginated(\n", " dataset_id=simpleqa_dataset_id,\n", " rows_in_page=5,\n", - ")" + ")\n" ] }, { @@ -1209,7 +1213,7 @@ "client.eval_tasks.register(\n", " eval_task_id=\"meta-reference::simpleqa\",\n", " dataset_id=simpleqa_dataset_id,\n", - " scoring_functions=[\"llm-as-judge::405b-simpleqa\"]\n", + " scoring_functions=[\"llm-as-judge::405b-simpleqa\"],\n", ")\n", "\n", "response = client.eval.evaluate_rows(\n", @@ -1222,15 +1226,16 @@ " \"type\": \"model\",\n", " \"model\": \"meta-llama/Llama-3.2-90B-Vision-Instruct\",\n", " \"sampling_params\": {\n", - " \"temperature\": 0.0,\n", + " \"strategy\": {\n", + " \"type\": \"greedy\",\n", + " },\n", " \"max_tokens\": 4096,\n", - " \"top_p\": 0.9,\n", " \"repeat_penalty\": 1.0,\n", " },\n", - " }\n", - " }\n", + " },\n", + " },\n", ")\n", - "pprint(response)" + "pprint(response)\n" ] }, { @@ -1347,23 +1352,19 @@ "agent_config = {\n", " \"model\": \"meta-llama/Llama-3.1-405B-Instruct\",\n", " \"instructions\": \"You are a helpful assistant\",\n", - " \"sampling_params\": {\n", - " \"strategy\": \"greedy\",\n", - " \"temperature\": 0.0,\n", - " \"top_p\": 0.95,\n", - " },\n", + " \"sampling_params\": {\"strategy\": {\"type\": \"greedy\"}},\n", " \"tools\": [\n", " {\n", " \"type\": \"brave_search\",\n", " \"engine\": \"tavily\",\n", - " \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\")\n", + " \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\"),\n", " }\n", " ],\n", " \"tool_choice\": \"auto\",\n", " \"tool_prompt_format\": \"json\",\n", " \"input_shields\": [],\n", " \"output_shields\": [],\n", - " \"enable_session_persistence\": False\n", + " \"enable_session_persistence\": False,\n", "}\n", "\n", "response = client.eval.evaluate_rows(\n", @@ -1375,10 +1376,10 @@ " \"eval_candidate\": {\n", " \"type\": \"agent\",\n", " \"config\": agent_config,\n", - " }\n", - " }\n", + " },\n", + " },\n", ")\n", - "pprint(response)" + "pprint(response)\n" ] } ], diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index 7e6284628..472e800a6 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -1336,6 +1336,7 @@ ], "source": [ "from rich.pretty import pprint\n", + "\n", "print(\"Available models:\")\n", "for m in client.models.list():\n", " print(f\"{m.identifier} (provider's alias: {m.provider_resource_id}) \")\n", @@ -1344,7 +1345,7 @@ "print(\"Available shields (safety models):\")\n", "for s in client.shields.list():\n", " print(s.identifier)\n", - "print(\"----\")" + "print(\"----\")\n" ] }, { @@ -1389,7 +1390,7 @@ "source": [ "model_id = \"meta-llama/Llama-3.1-70B-Instruct\"\n", "\n", - "model_id" + "model_id\n" ] }, { @@ -1432,11 +1433,11 @@ " model_id=model_id,\n", " messages=[\n", " {\"role\": \"system\", \"content\": \"You are a friendly assistant.\"},\n", - " {\"role\": \"user\", \"content\": \"Write a two-sentence poem about llama.\"}\n", + " {\"role\": \"user\", \"content\": \"Write a two-sentence poem about llama.\"},\n", " ],\n", ")\n", "\n", - "print(response.completion_message.content)" + "print(response.completion_message.content)\n" ] }, { @@ -1489,12 +1490,13 @@ "source": [ "from termcolor import cprint\n", "\n", + "\n", "def chat_loop():\n", " conversation_history = []\n", " while True:\n", - " user_input = input('User> ')\n", - " if user_input.lower() in ['exit', 'quit', 'bye']:\n", - " cprint('Ending conversation. Goodbye!', 'yellow')\n", + " user_input = input(\"User> \")\n", + " if user_input.lower() in [\"exit\", \"quit\", \"bye\"]:\n", + " cprint(\"Ending conversation. Goodbye!\", \"yellow\")\n", " break\n", "\n", " user_message = {\"role\": \"user\", \"content\": user_input}\n", @@ -1504,15 +1506,16 @@ " messages=conversation_history,\n", " model_id=model_id,\n", " )\n", - " cprint(f'> Response: {response.completion_message.content}', 'cyan')\n", + " cprint(f\"> Response: {response.completion_message.content}\", \"cyan\")\n", "\n", " assistant_message = {\n", - " \"role\": \"assistant\", # was user\n", + " \"role\": \"assistant\", # was user\n", " \"content\": response.completion_message.content,\n", " \"stop_reason\": response.completion_message.stop_reason,\n", " }\n", " conversation_history.append(assistant_message)\n", "\n", + "\n", "chat_loop()\n" ] }, @@ -1568,21 +1571,18 @@ "source": [ "from llama_stack_client.lib.inference.event_logger import EventLogger\n", "\n", - "message = {\n", - " \"role\": \"user\",\n", - " \"content\": 'Write me a sonnet about llama'\n", - "}\n", - "print(f'User> {message[\"content\"]}', 'green')\n", + "message = {\"role\": \"user\", \"content\": \"Write me a sonnet about llama\"}\n", + "print(f'User> {message[\"content\"]}', \"green\")\n", "\n", "response = client.inference.chat_completion(\n", " messages=[message],\n", " model_id=model_id,\n", - " stream=True, # <-----------\n", + " stream=True, # <-----------\n", ")\n", "\n", "# Print the tokens while they are received\n", "for log in EventLogger().log(response):\n", - " log.print()" + " log.print()\n" ] }, { @@ -1648,17 +1648,22 @@ "source": [ "from pydantic import BaseModel\n", "\n", + "\n", "class Output(BaseModel):\n", " name: str\n", " year_born: str\n", " year_retired: str\n", "\n", + "\n", "user_input = \"Michael Jordan was born in 1963. He played basketball for the Chicago Bulls. He retired in 2003. Extract this information into JSON for me. \"\n", "response = client.inference.completion(\n", " model_id=model_id,\n", " content=user_input,\n", " stream=False,\n", " sampling_params={\n", + " \"strategy\": {\n", + " \"type\": \"greedy\",\n", + " },\n", " \"max_tokens\": 50,\n", " },\n", " response_format={\n", @@ -1667,7 +1672,7 @@ " },\n", ")\n", "\n", - "pprint(response)" + "pprint(response)\n" ] }, { @@ -1823,7 +1828,7 @@ " shield_id=available_shields[0],\n", " params={},\n", " )\n", - " pprint(response)" + " pprint(response)\n" ] }, { @@ -2025,7 +2030,7 @@ "\n", "session_id = agent.create_session(\"test-session\")\n", "for prompt in user_prompts:\n", - " cprint(f'User> {prompt}', 'green')\n", + " cprint(f\"User> {prompt}\", \"green\")\n", " response = agent.create_turn(\n", " messages=[\n", " {\n", @@ -2451,8 +2456,8 @@ } ], "source": [ - "import pandas as pd\n", "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", "\n", "# Load data\n", "df = pd.read_csv(\"/tmp/tmpvzjigv7g/n2OzlTWhinflation.csv\")\n", @@ -2536,10 +2541,10 @@ } ], "source": [ + "from google.colab import userdata\n", "from llama_stack_client.lib.agents.agent import Agent\n", "from llama_stack_client.lib.agents.event_logger import EventLogger\n", "from llama_stack_client.types.agent_create_params import AgentConfig\n", - "from google.colab import userdata\n", "\n", "agent_config = AgentConfig(\n", " model=\"meta-llama/Llama-3.1-405B-Instruct-FP8\",\n", @@ -2570,7 +2575,7 @@ " )\n", "\n", " for log in EventLogger().log(response):\n", - " log.print()" + " log.print()\n" ] }, { @@ -2790,20 +2795,21 @@ "source": [ "print(f\"Getting traces for session_id={session_id}\")\n", "import json\n", + "\n", "from rich.pretty import pprint\n", "\n", "agent_logs = []\n", "\n", "for span in client.telemetry.query_spans(\n", " attribute_filters=[\n", - " {\"key\": \"session_id\", \"op\": \"eq\", \"value\": session_id},\n", + " {\"key\": \"session_id\", \"op\": \"eq\", \"value\": session_id},\n", " ],\n", - " attributes_to_return=[\"input\", \"output\"]\n", - " ):\n", - " if span.attributes[\"output\"] != \"no shields\":\n", - " agent_logs.append(span.attributes)\n", + " attributes_to_return=[\"input\", \"output\"],\n", + "):\n", + " if span.attributes[\"output\"] != \"no shields\":\n", + " agent_logs.append(span.attributes)\n", "\n", - "pprint(agent_logs)" + "pprint(agent_logs)\n" ] }, { @@ -2914,23 +2920,25 @@ "eval_rows = []\n", "\n", "for log in agent_logs:\n", - " last_msg = log['input'][-1]\n", - " if \"\\\"role\\\":\\\"user\\\"\" in last_msg:\n", - " eval_rows.append(\n", - " {\n", - " \"input_query\": last_msg,\n", - " \"generated_answer\": log[\"output\"],\n", - " # check if generated_answer uses tools brave_search\n", - " \"expected_answer\": \"brave_search\",\n", - " },\n", - " )\n", + " last_msg = log[\"input\"][-1]\n", + " if '\"role\":\"user\"' in last_msg:\n", + " eval_rows.append(\n", + " {\n", + " \"input_query\": last_msg,\n", + " \"generated_answer\": log[\"output\"],\n", + " # check if generated_answer uses tools brave_search\n", + " \"expected_answer\": \"brave_search\",\n", + " },\n", + " )\n", "\n", "pprint(eval_rows)\n", "scoring_params = {\n", " \"basic::subset_of\": None,\n", "}\n", - "scoring_response = client.scoring.score(input_rows=eval_rows, scoring_functions=scoring_params)\n", - "pprint(scoring_response)" + "scoring_response = client.scoring.score(\n", + " input_rows=eval_rows, scoring_functions=scoring_params\n", + ")\n", + "pprint(scoring_response)\n" ] }, { @@ -3031,7 +3039,9 @@ "EXPECTED_RESPONSE: {expected_answer}\n", "\"\"\"\n", "\n", - "input_query = \"What are the top 5 topics that were explained? Only list succinct bullet points.\"\n", + "input_query = (\n", + " \"What are the top 5 topics that were explained? Only list succinct bullet points.\"\n", + ")\n", "generated_answer = \"\"\"\n", "Here are the top 5 topics that were explained in the documentation for Torchtune:\n", "\n", @@ -3062,7 +3072,7 @@ "}\n", "\n", "response = client.scoring.score(input_rows=rows, scoring_functions=scoring_params)\n", - "pprint(response)" + "pprint(response)\n" ] } ], diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index 5ed8701a4..ad210a502 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -3514,6 +3514,20 @@ "tool_calls" ] }, + "GreedySamplingStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "greedy", + "default": "greedy" + } + }, + "additionalProperties": false, + "required": [ + "type" + ] + }, "ImageContentItem": { "type": "object", "properties": { @@ -3581,20 +3595,17 @@ "type": "object", "properties": { "strategy": { - "$ref": "#/components/schemas/SamplingStrategy", - "default": "greedy" - }, - "temperature": { - "type": "number", - "default": 0.0 - }, - "top_p": { - "type": "number", - "default": 0.95 - }, - "top_k": { - "type": "integer", - "default": 0 + "oneOf": [ + { + "$ref": "#/components/schemas/GreedySamplingStrategy" + }, + { + "$ref": "#/components/schemas/TopPSamplingStrategy" + }, + { + "$ref": "#/components/schemas/TopKSamplingStrategy" + } + ] }, "max_tokens": { "type": "integer", @@ -3610,14 +3621,6 @@ "strategy" ] }, - "SamplingStrategy": { - "type": "string", - "enum": [ - "greedy", - "top_p", - "top_k" - ] - }, "StopReason": { "type": "string", "enum": [ @@ -3871,6 +3874,45 @@ "content" ] }, + "TopKSamplingStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "top_k", + "default": "top_k" + }, + "top_k": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "type", + "top_k" + ] + }, + "TopPSamplingStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "top_p", + "default": "top_p" + }, + "temperature": { + "type": "number" + }, + "top_p": { + "type": "number", + "default": 0.95 + } + }, + "additionalProperties": false, + "required": [ + "type" + ] + }, "URL": { "type": "object", "properties": { @@ -8887,6 +8929,10 @@ "name": "GraphMemoryBankParams", "description": "" }, + { + "name": "GreedySamplingStrategy", + "description": "" + }, { "name": "HealthInfo", "description": "" @@ -9136,10 +9182,6 @@ "name": "SamplingParams", "description": "" }, - { - "name": "SamplingStrategy", - "description": "" - }, { "name": "SaveSpansToDatasetRequest", "description": "" @@ -9317,6 +9359,14 @@ { "name": "ToolRuntime" }, + { + "name": "TopKSamplingStrategy", + "description": "" + }, + { + "name": "TopPSamplingStrategy", + "description": "" + }, { "name": "Trace", "description": "" @@ -9456,6 +9506,7 @@ "GetSpanTreeRequest", "GraphMemoryBank", "GraphMemoryBankParams", + "GreedySamplingStrategy", "HealthInfo", "ImageContentItem", "InferenceStep", @@ -9513,7 +9564,6 @@ "RunShieldResponse", "SafetyViolation", "SamplingParams", - "SamplingStrategy", "SaveSpansToDatasetRequest", "ScoreBatchRequest", "ScoreBatchResponse", @@ -9553,6 +9603,8 @@ "ToolPromptFormat", "ToolResponse", "ToolResponseMessage", + "TopKSamplingStrategy", + "TopPSamplingStrategy", "Trace", "TrainingConfig", "Turn", diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index 2a573959f..8c885b7e5 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -937,6 +937,16 @@ components: required: - memory_bank_type type: object + GreedySamplingStrategy: + additionalProperties: false + properties: + type: + const: greedy + default: greedy + type: string + required: + - type + type: object HealthInfo: additionalProperties: false properties: @@ -2064,26 +2074,13 @@ components: default: 1.0 type: number strategy: - $ref: '#/components/schemas/SamplingStrategy' - default: greedy - temperature: - default: 0.0 - type: number - top_k: - default: 0 - type: integer - top_p: - default: 0.95 - type: number + oneOf: + - $ref: '#/components/schemas/GreedySamplingStrategy' + - $ref: '#/components/schemas/TopPSamplingStrategy' + - $ref: '#/components/schemas/TopKSamplingStrategy' required: - strategy type: object - SamplingStrategy: - enum: - - greedy - - top_p - - top_k - type: string SaveSpansToDatasetRequest: additionalProperties: false properties: @@ -2931,6 +2928,34 @@ components: - tool_name - content type: object + TopKSamplingStrategy: + additionalProperties: false + properties: + top_k: + type: integer + type: + const: top_k + default: top_k + type: string + required: + - type + - top_k + type: object + TopPSamplingStrategy: + additionalProperties: false + properties: + temperature: + type: number + top_p: + default: 0.95 + type: number + type: + const: top_p + default: top_p + type: string + required: + - type + type: object Trace: additionalProperties: false properties: @@ -5587,6 +5612,9 @@ tags: - description: name: GraphMemoryBankParams +- description: + name: GreedySamplingStrategy - description: name: HealthInfo - description: name: SamplingParams -- description: - name: SamplingStrategy - description: name: SaveSpansToDatasetRequest @@ -5874,6 +5899,12 @@ tags: /> name: ToolResponseMessage - name: ToolRuntime +- description: + name: TopKSamplingStrategy +- description: + name: TopPSamplingStrategy - description: name: Trace - description: @@ -5990,6 +6021,7 @@ x-tagGroups: - GetSpanTreeRequest - GraphMemoryBank - GraphMemoryBankParams + - GreedySamplingStrategy - HealthInfo - ImageContentItem - InferenceStep @@ -6047,7 +6079,6 @@ x-tagGroups: - RunShieldResponse - SafetyViolation - SamplingParams - - SamplingStrategy - SaveSpansToDatasetRequest - ScoreBatchRequest - ScoreBatchResponse @@ -6087,6 +6118,8 @@ x-tagGroups: - ToolPromptFormat - ToolResponse - ToolResponseMessage + - TopKSamplingStrategy + - TopPSamplingStrategy - Trace - TrainingConfig - Turn diff --git a/docs/source/benchmark_evaluations/index.md b/docs/source/benchmark_evaluations/index.md index 240555936..56852c89c 100644 --- a/docs/source/benchmark_evaluations/index.md +++ b/docs/source/benchmark_evaluations/index.md @@ -56,9 +56,10 @@ response = client.eval.evaluate_rows( "type": "model", "model": "meta-llama/Llama-3.2-90B-Vision-Instruct", "sampling_params": { - "temperature": 0.0, + "strategy": { + "type": "greedy", + }, "max_tokens": 4096, - "top_p": 0.9, "repeat_penalty": 1.0, }, "system_message": system_message @@ -113,9 +114,10 @@ response = client.eval.evaluate_rows( "type": "model", "model": "meta-llama/Llama-3.2-90B-Vision-Instruct", "sampling_params": { - "temperature": 0.0, + "strategy": { + "type": "greedy", + }, "max_tokens": 4096, - "top_p": 0.9, "repeat_penalty": 1.0, }, } @@ -134,9 +136,9 @@ agent_config = { "model": "meta-llama/Llama-3.1-405B-Instruct", "instructions": "You are a helpful assistant", "sampling_params": { - "strategy": "greedy", - "temperature": 0.0, - "top_p": 0.95, + "strategy": { + "type": "greedy", + }, }, "tools": [ { diff --git a/docs/source/building_applications/index.md b/docs/source/building_applications/index.md index acc19b515..61b7038cd 100644 --- a/docs/source/building_applications/index.md +++ b/docs/source/building_applications/index.md @@ -189,7 +189,11 @@ agent_config = AgentConfig( # Control the inference loop max_infer_iters=5, sampling_params={ - "temperature": 0.7, + "strategy": { + "type": "top_p", + "temperature": 0.7, + "top_p": 0.95 + }, "max_tokens": 2048 } ) diff --git a/docs/source/references/evals_reference/index.md b/docs/source/references/evals_reference/index.md index f93b56e64..c01fd69d8 100644 --- a/docs/source/references/evals_reference/index.md +++ b/docs/source/references/evals_reference/index.md @@ -92,9 +92,10 @@ response = client.eval.evaluate_rows( "type": "model", "model": "meta-llama/Llama-3.2-90B-Vision-Instruct", "sampling_params": { - "temperature": 0.0, + "strategy": { + "type": "greedy", + }, "max_tokens": 4096, - "top_p": 0.9, "repeat_penalty": 1.0, }, "system_message": system_message @@ -149,9 +150,10 @@ response = client.eval.evaluate_rows( "type": "model", "model": "meta-llama/Llama-3.2-90B-Vision-Instruct", "sampling_params": { - "temperature": 0.0, + "strategy": { + "type": "greedy", + }, "max_tokens": 4096, - "top_p": 0.9, "repeat_penalty": 1.0, }, } @@ -170,9 +172,9 @@ agent_config = { "model": "meta-llama/Llama-3.1-405B-Instruct", "instructions": "You are a helpful assistant", "sampling_params": { - "strategy": "greedy", - "temperature": 0.0, - "top_p": 0.95, + "strategy": { + "type": "greedy", + }, }, "tools": [ { @@ -318,10 +320,9 @@ The `EvalTaskConfig` are user specified config to define: "type": "model", "model": "Llama3.2-3B-Instruct", "sampling_params": { - "strategy": "greedy", - "temperature": 0, - "top_p": 0.95, - "top_k": 0, + "strategy": { + "type": "greedy", + }, "max_tokens": 0, "repetition_penalty": 1.0 } @@ -337,10 +338,9 @@ The `EvalTaskConfig` are user specified config to define: "type": "model", "model": "Llama3.1-405B-Instruct", "sampling_params": { - "strategy": "greedy", - "temperature": 0, - "top_p": 0.95, - "top_k": 0, + "strategy": { + "type": "greedy", + }, "max_tokens": 0, "repetition_penalty": 1.0 } diff --git a/docs/source/references/llama_cli_reference/index.md b/docs/source/references/llama_cli_reference/index.md index a0314644a..f7ac5fe36 100644 --- a/docs/source/references/llama_cli_reference/index.md +++ b/docs/source/references/llama_cli_reference/index.md @@ -214,7 +214,6 @@ llama model describe -m Llama3.2-3B-Instruct | | } | +-----------------------------+----------------------------------+ | Recommended sampling params | { | -| | "strategy": "top_p", | | | "temperature": 1.0, | | | "top_p": 0.9, | | | "top_k": 0 | diff --git a/docs/source/references/llama_stack_client_cli_reference.md b/docs/source/references/llama_stack_client_cli_reference.md index b35aa189d..c3abccfd9 100644 --- a/docs/source/references/llama_stack_client_cli_reference.md +++ b/docs/source/references/llama_stack_client_cli_reference.md @@ -200,10 +200,9 @@ Example eval_task_config.json: "type": "model", "model": "Llama3.1-405B-Instruct", "sampling_params": { - "strategy": "greedy", - "temperature": 0, - "top_p": 0.95, - "top_k": 0, + "strategy": { + "type": "greedy" + }, "max_tokens": 0, "repetition_penalty": 1.0 } diff --git a/docs/zero_to_hero_guide/04_Tool_Calling101.ipynb b/docs/zero_to_hero_guide/04_Tool_Calling101.ipynb index 4f0d2e887..4c278493b 100644 --- a/docs/zero_to_hero_guide/04_Tool_Calling101.ipynb +++ b/docs/zero_to_hero_guide/04_Tool_Calling101.ipynb @@ -26,27 +26,28 @@ "metadata": {}, "outputs": [], "source": [ - "import os\n", - "import requests\n", - "import json\n", "import asyncio\n", - "import nest_asyncio\n", + "import json\n", + "import os\n", "from typing import Dict, List\n", + "\n", + "import nest_asyncio\n", + "import requests\n", "from dotenv import load_dotenv\n", "from llama_stack_client import LlamaStackClient\n", - "from llama_stack_client.lib.agents.custom_tool import CustomTool\n", - "from llama_stack_client.types.shared.tool_response_message import ToolResponseMessage\n", - "from llama_stack_client.types import CompletionMessage\n", "from llama_stack_client.lib.agents.agent import Agent\n", + "from llama_stack_client.lib.agents.custom_tool import CustomTool\n", "from llama_stack_client.lib.agents.event_logger import EventLogger\n", + "from llama_stack_client.types import CompletionMessage\n", "from llama_stack_client.types.agent_create_params import AgentConfig\n", + "from llama_stack_client.types.shared.tool_response_message import ToolResponseMessage\n", "\n", "# Allow asyncio to run in Jupyter Notebook\n", "nest_asyncio.apply()\n", "\n", - "HOST='localhost'\n", - "PORT=5001\n", - "MODEL_NAME='meta-llama/Llama-3.2-3B-Instruct'" + "HOST = \"localhost\"\n", + "PORT = 5001\n", + "MODEL_NAME = \"meta-llama/Llama-3.2-3B-Instruct\"\n" ] }, { @@ -69,7 +70,7 @@ "outputs": [], "source": [ "load_dotenv()\n", - "BRAVE_SEARCH_API_KEY = os.environ['BRAVE_SEARCH_API_KEY']" + "BRAVE_SEARCH_API_KEY = os.environ[\"BRAVE_SEARCH_API_KEY\"]\n" ] }, { @@ -118,7 +119,7 @@ " cleaned = {k: v for k, v in results[idx].items() if k in selected_keys}\n", " clean_response.append(cleaned)\n", "\n", - " return {\"query\": query, \"top_k\": clean_response}" + " return {\"query\": query, \"top_k\": clean_response}\n" ] }, { @@ -157,25 +158,29 @@ " for message in messages:\n", " if isinstance(message, CompletionMessage) and message.tool_calls:\n", " for tool_call in message.tool_calls:\n", - " if 'query' in tool_call.arguments:\n", - " query = tool_call.arguments['query']\n", + " if \"query\" in tool_call.arguments:\n", + " query = tool_call.arguments[\"query\"]\n", " call_id = tool_call.call_id\n", "\n", " if query:\n", " search_result = await self.run_impl(query)\n", - " return [ToolResponseMessage(\n", - " call_id=call_id,\n", - " role=\"ipython\",\n", - " content=self._format_response_for_agent(search_result),\n", - " tool_name=\"brave_search\"\n", - " )]\n", + " return [\n", + " ToolResponseMessage(\n", + " call_id=call_id,\n", + " role=\"ipython\",\n", + " content=self._format_response_for_agent(search_result),\n", + " tool_name=\"brave_search\",\n", + " )\n", + " ]\n", "\n", - " return [ToolResponseMessage(\n", - " call_id=\"no_call_id\",\n", - " role=\"ipython\",\n", - " content=\"No query provided.\",\n", - " tool_name=\"brave_search\"\n", - " )]\n", + " return [\n", + " ToolResponseMessage(\n", + " call_id=\"no_call_id\",\n", + " role=\"ipython\",\n", + " content=\"No query provided.\",\n", + " tool_name=\"brave_search\",\n", + " )\n", + " ]\n", "\n", " def _format_response_for_agent(self, search_result):\n", " parsed_result = json.loads(search_result)\n", @@ -186,7 +191,7 @@ " f\" URL: {result.get('url', 'No URL')}\\n\"\n", " f\" Description: {result.get('description', 'No Description')}\\n\\n\"\n", " )\n", - " return formatted_result" + " return formatted_result\n" ] }, { @@ -209,7 +214,7 @@ "async def execute_search(query: str):\n", " web_search_tool = WebSearchTool(api_key=BRAVE_SEARCH_API_KEY)\n", " result = await web_search_tool.run_impl(query)\n", - " print(\"Search Results:\", result)" + " print(\"Search Results:\", result)\n" ] }, { @@ -236,7 +241,7 @@ ], "source": [ "query = \"Latest developments in quantum computing\"\n", - "asyncio.run(execute_search(query))" + "asyncio.run(execute_search(query))\n" ] }, { @@ -288,19 +293,17 @@ "\n", " # Initialize custom tool (ensure `WebSearchTool` is defined earlier in the notebook)\n", " webSearchTool = WebSearchTool(api_key=BRAVE_SEARCH_API_KEY)\n", - " \n", + "\n", " # Define the agent configuration, including the model and tool setup\n", " agent_config = AgentConfig(\n", " model=MODEL_NAME,\n", " instructions=\"\"\"You are a helpful assistant that responds to user queries with relevant information and cites sources when available.\"\"\",\n", " sampling_params={\n", - " \"strategy\": \"greedy\",\n", - " \"temperature\": 1.0,\n", - " \"top_p\": 0.9,\n", + " \"strategy\": {\n", + " \"type\": \"greedy\",\n", + " },\n", " },\n", - " tools=[\n", - " webSearchTool.get_tool_definition()\n", - " ],\n", + " tools=[webSearchTool.get_tool_definition()],\n", " tool_choice=\"auto\",\n", " tool_prompt_format=\"python_list\",\n", " input_shields=input_shields,\n", @@ -329,8 +332,9 @@ " async for log in EventLogger().log(response):\n", " log.print()\n", "\n", + "\n", "# Run the function asynchronously in a Jupyter Notebook cell\n", - "await run_main(disable_safety=True)" + "await run_main(disable_safety=True)\n" ] } ], diff --git a/docs/zero_to_hero_guide/07_Agents101.ipynb b/docs/zero_to_hero_guide/07_Agents101.ipynb index 88b73b4cd..04178f3f6 100644 --- a/docs/zero_to_hero_guide/07_Agents101.ipynb +++ b/docs/zero_to_hero_guide/07_Agents101.ipynb @@ -50,8 +50,8 @@ "outputs": [], "source": [ "HOST = \"localhost\" # Replace with your host\n", - "PORT = 5001 # Replace with your port\n", - "MODEL_NAME='meta-llama/Llama-3.2-3B-Instruct'" + "PORT = 5001 # Replace with your port\n", + "MODEL_NAME = \"meta-llama/Llama-3.2-3B-Instruct\"\n" ] }, { @@ -60,10 +60,12 @@ "metadata": {}, "outputs": [], "source": [ - "from dotenv import load_dotenv\n", "import os\n", + "\n", + "from dotenv import load_dotenv\n", + "\n", "load_dotenv()\n", - "BRAVE_SEARCH_API_KEY = os.environ['BRAVE_SEARCH_API_KEY']" + "BRAVE_SEARCH_API_KEY = os.environ[\"BRAVE_SEARCH_API_KEY\"]\n" ] }, { @@ -104,20 +106,22 @@ ], "source": [ "import os\n", + "\n", "from llama_stack_client import LlamaStackClient\n", "from llama_stack_client.lib.agents.agent import Agent\n", "from llama_stack_client.lib.agents.event_logger import EventLogger\n", "from llama_stack_client.types.agent_create_params import AgentConfig\n", "\n", + "\n", "async def agent_example():\n", " client = LlamaStackClient(base_url=f\"http://{HOST}:{PORT}\")\n", " agent_config = AgentConfig(\n", " model=MODEL_NAME,\n", " instructions=\"You are a helpful assistant! If you call builtin tools like brave search, follow the syntax brave_search.call(…)\",\n", " sampling_params={\n", - " \"strategy\": \"greedy\",\n", - " \"temperature\": 1.0,\n", - " \"top_p\": 0.9,\n", + " \"strategy\": {\n", + " \"type\": \"greedy\",\n", + " },\n", " },\n", " tools=[\n", " {\n", @@ -157,7 +161,7 @@ " log.print()\n", "\n", "\n", - "await agent_example()" + "await agent_example()\n" ] }, { diff --git a/docs/zero_to_hero_guide/README.md b/docs/zero_to_hero_guide/README.md index f96ae49ce..c4803a1d6 100644 --- a/docs/zero_to_hero_guide/README.md +++ b/docs/zero_to_hero_guide/README.md @@ -157,7 +157,15 @@ curl http://localhost:$LLAMA_STACK_PORT/alpha/inference/chat-completion {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Write me a 2-sentence poem about the moon"} ], - "sampling_params": {"temperature": 0.7, "seed": 42, "max_tokens": 512} + "sampling_params": { + "strategy": { + "type": "top_p", + "temperatrue": 0.7, + "top_p": 0.95, + }, + "seed": 42, + "max_tokens": 512 + } } EOF ``` diff --git a/docs/zero_to_hero_guide/Tool_Calling101_Using_Together's_Llama_Stack_Server.ipynb b/docs/zero_to_hero_guide/Tool_Calling101_Using_Together's_Llama_Stack_Server.ipynb index b21f3d64c..68e781018 100644 --- a/docs/zero_to_hero_guide/Tool_Calling101_Using_Together's_Llama_Stack_Server.ipynb +++ b/docs/zero_to_hero_guide/Tool_Calling101_Using_Together's_Llama_Stack_Server.ipynb @@ -83,8 +83,8 @@ }, "outputs": [], "source": [ - "LLAMA_STACK_API_TOGETHER_URL=\"https://llama-stack.together.ai\"\n", - "LLAMA31_8B_INSTRUCT = \"Llama3.1-8B-Instruct\"" + "LLAMA_STACK_API_TOGETHER_URL = \"https://llama-stack.together.ai\"\n", + "LLAMA31_8B_INSTRUCT = \"Llama3.1-8B-Instruct\"\n" ] }, { @@ -107,12 +107,13 @@ " AgentConfigToolSearchToolDefinition,\n", ")\n", "\n", + "\n", "# Helper function to create an agent with tools\n", "async def create_tool_agent(\n", " client: LlamaStackClient,\n", " tools: List[Dict],\n", " instructions: str = \"You are a helpful assistant\",\n", - " model: str = LLAMA31_8B_INSTRUCT\n", + " model: str = LLAMA31_8B_INSTRUCT,\n", ") -> Agent:\n", " \"\"\"Create an agent with specified tools.\"\"\"\n", " print(\"Using the following model: \", model)\n", @@ -120,9 +121,9 @@ " model=model,\n", " instructions=instructions,\n", " sampling_params={\n", - " \"strategy\": \"greedy\",\n", - " \"temperature\": 1.0,\n", - " \"top_p\": 0.9,\n", + " \"strategy\": {\n", + " \"type\": \"greedy\",\n", + " },\n", " },\n", " tools=tools,\n", " tool_choice=\"auto\",\n", @@ -130,7 +131,7 @@ " enable_session_persistence=True,\n", " )\n", "\n", - " return Agent(client, agent_config)" + " return Agent(client, agent_config)\n" ] }, { @@ -172,7 +173,8 @@ ], "source": [ "# comment this if you don't have a BRAVE_SEARCH_API_KEY\n", - "os.environ[\"BRAVE_SEARCH_API_KEY\"] = 'YOUR_BRAVE_SEARCH_API_KEY'\n", + "os.environ[\"BRAVE_SEARCH_API_KEY\"] = \"YOUR_BRAVE_SEARCH_API_KEY\"\n", + "\n", "\n", "async def create_search_agent(client: LlamaStackClient) -> Agent:\n", " \"\"\"Create an agent with Brave Search capability.\"\"\"\n", @@ -186,8 +188,8 @@ "\n", " return await create_tool_agent(\n", " client=client,\n", - " tools=[search_tool], # set this to [] if you don't have a BRAVE_SEARCH_API_KEY\n", - " model = LLAMA31_8B_INSTRUCT,\n", + " tools=[search_tool], # set this to [] if you don't have a BRAVE_SEARCH_API_KEY\n", + " model=LLAMA31_8B_INSTRUCT,\n", " instructions=\"\"\"\n", " You are a research assistant that can search the web.\n", " Always cite your sources with URLs when providing information.\n", @@ -198,9 +200,10 @@ "\n", " SOURCES:\n", " - [Source title](URL)\n", - " \"\"\"\n", + " \"\"\",\n", " )\n", "\n", + "\n", "# Example usage\n", "async def search_example():\n", " client = LlamaStackClient(base_url=LLAMA_STACK_API_TOGETHER_URL)\n", @@ -212,7 +215,7 @@ " # Example queries\n", " queries = [\n", " \"What are the latest developments in quantum computing?\",\n", - " #\"Who won the most recent Super Bowl?\",\n", + " # \"Who won the most recent Super Bowl?\",\n", " ]\n", "\n", " for query in queries:\n", @@ -227,8 +230,9 @@ " async for log in EventLogger().log(response):\n", " log.print()\n", "\n", + "\n", "# Run the example (in Jupyter, use asyncio.run())\n", - "await search_example()" + "await search_example()\n" ] }, { @@ -286,12 +290,16 @@ } ], "source": [ - "from typing import TypedDict, Optional, Dict, Any\n", - "from datetime import datetime\n", "import json\n", - "from llama_stack_client.types.tool_param_definition_param import ToolParamDefinitionParam\n", - "from llama_stack_client.types import CompletionMessage,ToolResponseMessage\n", + "from datetime import datetime\n", + "from typing import Any, Dict, Optional, TypedDict\n", + "\n", "from llama_stack_client.lib.agents.custom_tool import CustomTool\n", + "from llama_stack_client.types import CompletionMessage, ToolResponseMessage\n", + "from llama_stack_client.types.tool_param_definition_param import (\n", + " ToolParamDefinitionParam,\n", + ")\n", + "\n", "\n", "class WeatherTool(CustomTool):\n", " \"\"\"Example custom tool for weather information.\"\"\"\n", @@ -305,16 +313,15 @@ " def get_params_definition(self) -> Dict[str, ToolParamDefinitionParam]:\n", " return {\n", " \"location\": ToolParamDefinitionParam(\n", - " param_type=\"str\",\n", - " description=\"City or location name\",\n", - " required=True\n", + " param_type=\"str\", description=\"City or location name\", required=True\n", " ),\n", " \"date\": ToolParamDefinitionParam(\n", " param_type=\"str\",\n", " description=\"Optional date (YYYY-MM-DD)\",\n", - " required=False\n", - " )\n", + " required=False,\n", + " ),\n", " }\n", + "\n", " async def run(self, messages: List[CompletionMessage]) -> List[ToolResponseMessage]:\n", " assert len(messages) == 1, \"Expected single message\"\n", "\n", @@ -337,20 +344,14 @@ " )\n", " return [message]\n", "\n", - " async def run_impl(self, location: str, date: Optional[str] = None) -> Dict[str, Any]:\n", + " async def run_impl(\n", + " self, location: str, date: Optional[str] = None\n", + " ) -> Dict[str, Any]:\n", " \"\"\"Simulate getting weather data (replace with actual API call).\"\"\"\n", " # Mock implementation\n", " if date:\n", - " return {\n", - " \"temperature\": 90.1,\n", - " \"conditions\": \"sunny\",\n", - " \"humidity\": 40.0\n", - " }\n", - " return {\n", - " \"temperature\": 72.5,\n", - " \"conditions\": \"partly cloudy\",\n", - " \"humidity\": 65.0\n", - " }\n", + " return {\"temperature\": 90.1, \"conditions\": \"sunny\", \"humidity\": 40.0}\n", + " return {\"temperature\": 72.5, \"conditions\": \"partly cloudy\", \"humidity\": 65.0}\n", "\n", "\n", "async def create_weather_agent(client: LlamaStackClient) -> Agent:\n", @@ -358,38 +359,33 @@ "\n", " # Create the agent with the tool\n", " weather_tool = WeatherTool()\n", - " \n", + "\n", " agent_config = AgentConfig(\n", " model=LLAMA31_8B_INSTRUCT,\n", - " #model=model_name,\n", + " # model=model_name,\n", " instructions=\"\"\"\n", " You are a weather assistant that can provide weather information.\n", " Always specify the location clearly in your responses.\n", " Include both temperature and conditions in your summaries.\n", " \"\"\",\n", " sampling_params={\n", - " \"strategy\": \"greedy\",\n", - " \"temperature\": 1.0,\n", - " \"top_p\": 0.9,\n", + " \"strategy\": {\n", + " \"type\": \"greedy\",\n", + " },\n", " },\n", - " tools=[\n", - " weather_tool.get_tool_definition()\n", - " ],\n", + " tools=[weather_tool.get_tool_definition()],\n", " tool_choice=\"auto\",\n", " tool_prompt_format=\"json\",\n", " input_shields=[],\n", " output_shields=[],\n", - " enable_session_persistence=True\n", + " enable_session_persistence=True,\n", " )\n", "\n", - " agent = Agent(\n", - " client=client,\n", - " agent_config=agent_config,\n", - " custom_tools=[weather_tool]\n", - " )\n", + " agent = Agent(client=client, agent_config=agent_config, custom_tools=[weather_tool])\n", "\n", " return agent\n", "\n", + "\n", "# Example usage\n", "async def weather_example():\n", " client = LlamaStackClient(base_url=LLAMA_STACK_API_TOGETHER_URL)\n", @@ -413,12 +409,14 @@ " async for log in EventLogger().log(response):\n", " log.print()\n", "\n", + "\n", "# For Jupyter notebooks\n", "import nest_asyncio\n", + "\n", "nest_asyncio.apply()\n", "\n", "# Run the example\n", - "await weather_example()" + "await weather_example()\n" ] }, { diff --git a/llama_stack/cli/model/describe.py b/llama_stack/cli/model/describe.py index 70e72f7be..fc0190ca8 100644 --- a/llama_stack/cli/model/describe.py +++ b/llama_stack/cli/model/describe.py @@ -13,7 +13,6 @@ from termcolor import colored from llama_stack.cli.subcommand import Subcommand from llama_stack.cli.table import print_table -from llama_stack.distribution.utils.serialize import EnumEncoder class ModelDescribe(Subcommand): @@ -72,7 +71,7 @@ class ModelDescribe(Subcommand): rows.append( ( "Recommended sampling params", - json.dumps(sampling_params, cls=EnumEncoder, indent=4), + json.dumps(sampling_params, indent=4), ) ) diff --git a/llama_stack/distribution/ui/page/evaluations/native_eval.py b/llama_stack/distribution/ui/page/evaluations/native_eval.py index 2cbc8d63e..46839e2f9 100644 --- a/llama_stack/distribution/ui/page/evaluations/native_eval.py +++ b/llama_stack/distribution/ui/page/evaluations/native_eval.py @@ -58,11 +58,6 @@ def define_eval_candidate_2(): # Sampling Parameters st.markdown("##### Sampling Parameters") - strategy = st.selectbox( - "Strategy", - ["greedy", "top_p", "top_k"], - index=0, - ) temperature = st.slider( "Temperature", min_value=0.0, @@ -95,13 +90,20 @@ def define_eval_candidate_2(): help="Controls the likelihood for generating the same word or phrase multiple times in the same sentence or paragraph. 1 implies no penalty, 2 will strongly discourage model to repeat words or phrases.", ) if candidate_type == "model": + if temperature > 0.0: + strategy = { + "type": "top_p", + "temperature": temperature, + "top_p": top_p, + } + else: + strategy = {"type": "greedy"} + eval_candidate = { "type": "model", "model": selected_model, "sampling_params": { "strategy": strategy, - "temperature": temperature, - "top_p": top_p, "max_tokens": max_tokens, "repetition_penalty": repetition_penalty, }, diff --git a/llama_stack/distribution/ui/page/playground/chat.py b/llama_stack/distribution/ui/page/playground/chat.py index 0b8073756..5d91ec819 100644 --- a/llama_stack/distribution/ui/page/playground/chat.py +++ b/llama_stack/distribution/ui/page/playground/chat.py @@ -95,6 +95,15 @@ if prompt := st.chat_input("Example: What is Llama Stack?"): message_placeholder = st.empty() full_response = "" + if temperature > 0.0: + strategy = { + "type": "top_p", + "temperature": temperature, + "top_p": top_p, + } + else: + strategy = {"type": "greedy"} + response = llama_stack_api.client.inference.chat_completion( messages=[ {"role": "system", "content": system_prompt}, @@ -103,8 +112,7 @@ if prompt := st.chat_input("Example: What is Llama Stack?"): model_id=selected_model, stream=stream, sampling_params={ - "temperature": temperature, - "top_p": top_p, + "strategy": strategy, "max_tokens": max_tokens, "repetition_penalty": repetition_penalty, }, diff --git a/llama_stack/distribution/ui/page/playground/rag.py b/llama_stack/distribution/ui/page/playground/rag.py index 196c889ba..3a2ba1270 100644 --- a/llama_stack/distribution/ui/page/playground/rag.py +++ b/llama_stack/distribution/ui/page/playground/rag.py @@ -118,13 +118,20 @@ def rag_chat_page(): with st.chat_message(message["role"]): st.markdown(message["content"]) + if temperature > 0.0: + strategy = { + "type": "top_p", + "temperature": temperature, + "top_p": top_p, + } + else: + strategy = {"type": "greedy"} + agent_config = AgentConfig( model=selected_model, instructions=system_prompt, sampling_params={ - "strategy": "greedy", - "temperature": temperature, - "top_p": top_p, + "strategy": strategy, }, tools=[ { diff --git a/llama_stack/providers/inline/inference/meta_reference/generation.py b/llama_stack/providers/inline/inference/meta_reference/generation.py index 1807e4ad5..a96409cab 100644 --- a/llama_stack/providers/inline/inference/meta_reference/generation.py +++ b/llama_stack/providers/inline/inference/meta_reference/generation.py @@ -23,6 +23,11 @@ from fairscale.nn.model_parallel.initialize import ( initialize_model_parallel, model_parallel_is_initialized, ) +from llama_models.datatypes import ( + GreedySamplingStrategy, + SamplingParams, + TopPSamplingStrategy, +) from llama_models.llama3.api.args import ModelArgs from llama_models.llama3.api.chat_format import ChatFormat, LLMInput from llama_models.llama3.api.datatypes import Model @@ -363,11 +368,12 @@ class Llama: max_gen_len = self.model.params.max_seq_len - 1 model_input = self.formatter.encode_content(request.content) + temperature, top_p = _infer_sampling_params(sampling_params) yield from self.generate( model_input=model_input, max_gen_len=max_gen_len, - temperature=sampling_params.temperature, - top_p=sampling_params.top_p, + temperature=temperature, + top_p=top_p, logprobs=bool(request.logprobs), include_stop_token=True, logits_processor=get_logits_processor( @@ -390,14 +396,15 @@ class Llama: ): max_gen_len = self.model.params.max_seq_len - 1 + temperature, top_p = _infer_sampling_params(sampling_params) yield from self.generate( model_input=self.formatter.encode_dialog_prompt( request.messages, request.tool_prompt_format, ), max_gen_len=max_gen_len, - temperature=sampling_params.temperature, - top_p=sampling_params.top_p, + temperature=temperature, + top_p=top_p, logprobs=bool(request.logprobs), include_stop_token=True, logits_processor=get_logits_processor( @@ -492,3 +499,15 @@ def _build_regular_tokens_list( is_word_start_token = len(decoded_after_0) > len(decoded_regular) regular_tokens.append((token_idx, decoded_after_0, is_word_start_token)) return regular_tokens + + +def _infer_sampling_params(sampling_params: SamplingParams): + if isinstance(sampling_params.strategy, GreedySamplingStrategy): + temperature = 0.0 + top_p = 1.0 + elif isinstance(sampling_params.strategy, TopPSamplingStrategy): + temperature = sampling_params.strategy.temperature + top_p = sampling_params.strategy.top_p + else: + raise ValueError(f"Unsupported sampling strategy {sampling_params.strategy}") + return temperature, top_p diff --git a/llama_stack/providers/inline/inference/vllm/vllm.py b/llama_stack/providers/inline/inference/vllm/vllm.py index 0f1045845..49dd8316e 100644 --- a/llama_stack/providers/inline/inference/vllm/vllm.py +++ b/llama_stack/providers/inline/inference/vllm/vllm.py @@ -36,6 +36,7 @@ from llama_stack.apis.inference import ( from llama_stack.apis.models import Model from llama_stack.providers.datatypes import ModelsProtocolPrivate from llama_stack.providers.utils.inference.openai_compat import ( + get_sampling_options, OpenAICompatCompletionChoice, OpenAICompatCompletionResponse, process_chat_completion_response, @@ -126,21 +127,12 @@ class VLLMInferenceImpl(Inference, ModelsProtocolPrivate): if sampling_params is None: return VLLMSamplingParams(max_tokens=self.config.max_tokens) - # TODO convert what I saw in my first test ... but surely there's more to do here - kwargs = { - "temperature": sampling_params.temperature, - "max_tokens": self.config.max_tokens, - } - if sampling_params.top_k: - kwargs["top_k"] = sampling_params.top_k - if sampling_params.top_p: - kwargs["top_p"] = sampling_params.top_p - if sampling_params.max_tokens: - kwargs["max_tokens"] = sampling_params.max_tokens - if sampling_params.repetition_penalty > 0: - kwargs["repetition_penalty"] = sampling_params.repetition_penalty + options = get_sampling_options(sampling_params) + if "repeat_penalty" in options: + options["repetition_penalty"] = options["repeat_penalty"] + del options["repeat_penalty"] - return VLLMSamplingParams(**kwargs) + return VLLMSamplingParams(**options) async def unregister_model(self, model_id: str) -> None: pass diff --git a/llama_stack/providers/remote/inference/bedrock/bedrock.py b/llama_stack/providers/remote/inference/bedrock/bedrock.py index 59f30024e..10b51e86b 100644 --- a/llama_stack/providers/remote/inference/bedrock/bedrock.py +++ b/llama_stack/providers/remote/inference/bedrock/bedrock.py @@ -34,6 +34,7 @@ from llama_stack.providers.utils.inference.model_registry import ( ModelRegistryHelper, ) from llama_stack.providers.utils.inference.openai_compat import ( + get_sampling_strategy_options, OpenAICompatCompletionChoice, OpenAICompatCompletionResponse, process_chat_completion_response, @@ -166,16 +167,13 @@ class BedrockInferenceAdapter(ModelRegistryHelper, Inference): ) -> Dict: bedrock_model = request.model - inference_config = {} - param_mapping = { - "max_tokens": "max_gen_len", - "temperature": "temperature", - "top_p": "top_p", - } + sampling_params = request.sampling_params + options = get_sampling_strategy_options(sampling_params) - for k, v in param_mapping.items(): - if getattr(request.sampling_params, k): - inference_config[v] = getattr(request.sampling_params, k) + if sampling_params.max_tokens: + options["max_gen_len"] = sampling_params.max_tokens + if sampling_params.repetition_penalty > 0: + options["repetition_penalty"] = sampling_params.repetition_penalty prompt = await chat_completion_request_to_prompt( request, self.get_llama_model(request.model), self.formatter @@ -185,7 +183,7 @@ class BedrockInferenceAdapter(ModelRegistryHelper, Inference): "body": json.dumps( { "prompt": prompt, - **inference_config, + **options, } ), } diff --git a/llama_stack/providers/remote/inference/cerebras/cerebras.py b/llama_stack/providers/remote/inference/cerebras/cerebras.py index b78471787..0b6ce142c 100644 --- a/llama_stack/providers/remote/inference/cerebras/cerebras.py +++ b/llama_stack/providers/remote/inference/cerebras/cerebras.py @@ -9,6 +9,7 @@ from typing import AsyncGenerator, List, Optional, Union from cerebras.cloud.sdk import AsyncCerebras from llama_models.datatypes import CoreModelId from llama_models.llama3.api.chat_format import ChatFormat +from llama_models.llama3.api.datatypes import TopKSamplingStrategy from llama_models.llama3.api.tokenizer import Tokenizer from llama_stack.apis.common.content_types import InterleavedContent @@ -172,7 +173,9 @@ class CerebrasInferenceAdapter(ModelRegistryHelper, Inference): async def _get_params( self, request: Union[ChatCompletionRequest, CompletionRequest] ) -> dict: - if request.sampling_params and request.sampling_params.top_k: + if request.sampling_params and isinstance( + request.sampling_params.strategy, TopKSamplingStrategy + ): raise ValueError("`top_k` not supported by Cerebras") prompt = "" diff --git a/llama_stack/providers/remote/inference/groq/groq_utils.py b/llama_stack/providers/remote/inference/groq/groq_utils.py index 11f684847..b614c90f4 100644 --- a/llama_stack/providers/remote/inference/groq/groq_utils.py +++ b/llama_stack/providers/remote/inference/groq/groq_utils.py @@ -48,6 +48,9 @@ from llama_stack.apis.inference import ( ToolDefinition, ToolPromptFormat, ) +from llama_stack.providers.utils.inference.openai_compat import ( + get_sampling_strategy_options, +) def convert_chat_completion_request( @@ -77,6 +80,7 @@ def convert_chat_completion_request( if request.tool_prompt_format != ToolPromptFormat.json: warnings.warn("tool_prompt_format is not used by Groq. Ignoring.") + sampling_options = get_sampling_strategy_options(request.sampling_params) return CompletionCreateParams( model=request.model, messages=[_convert_message(message) for message in request.messages], @@ -84,8 +88,8 @@ def convert_chat_completion_request( frequency_penalty=None, stream=request.stream, max_tokens=request.sampling_params.max_tokens or None, - temperature=request.sampling_params.temperature, - top_p=request.sampling_params.top_p, + temperature=sampling_options.get("temperature", 1.0), + top_p=sampling_options.get("top_p", 1.0), tools=[_convert_groq_tool_definition(tool) for tool in request.tools or []], tool_choice=request.tool_choice.value if request.tool_choice else None, ) diff --git a/llama_stack/providers/remote/inference/nvidia/openai_utils.py b/llama_stack/providers/remote/inference/nvidia/openai_utils.py index 975812844..8db7f9197 100644 --- a/llama_stack/providers/remote/inference/nvidia/openai_utils.py +++ b/llama_stack/providers/remote/inference/nvidia/openai_utils.py @@ -8,6 +8,11 @@ import json import warnings from typing import Any, AsyncGenerator, Dict, Generator, List, Optional +from llama_models.datatypes import ( + GreedySamplingStrategy, + TopKSamplingStrategy, + TopPSamplingStrategy, +) from llama_models.llama3.api.datatypes import ( BuiltinTool, StopReason, @@ -263,19 +268,20 @@ def convert_chat_completion_request( if request.sampling_params.max_tokens: payload.update(max_tokens=request.sampling_params.max_tokens) - if request.sampling_params.strategy == "top_p": + strategy = request.sampling_params.strategy + if isinstance(strategy, TopPSamplingStrategy): nvext.update(top_k=-1) - payload.update(top_p=request.sampling_params.top_p) - elif request.sampling_params.strategy == "top_k": - if ( - request.sampling_params.top_k != -1 - and request.sampling_params.top_k < 1 - ): + payload.update(top_p=strategy.top_p) + payload.update(temperature=strategy.temperature) + elif isinstance(strategy, TopKSamplingStrategy): + if strategy.top_k != -1 and strategy.top_k < 1: warnings.warn("top_k must be -1 or >= 1") - nvext.update(top_k=request.sampling_params.top_k) - elif request.sampling_params.strategy == "greedy": + nvext.update(top_k=strategy.top_k) + elif isinstance(strategy, GreedySamplingStrategy): nvext.update(top_k=-1) - payload.update(temperature=request.sampling_params.temperature) + payload.update(temperature=strategy.temperature) + else: + raise ValueError(f"Unsupported sampling strategy: {strategy}") return payload diff --git a/llama_stack/providers/tests/agents/test_agents.py b/llama_stack/providers/tests/agents/test_agents.py index 27fb90572..320096826 100644 --- a/llama_stack/providers/tests/agents/test_agents.py +++ b/llama_stack/providers/tests/agents/test_agents.py @@ -7,6 +7,7 @@ import os import pytest +from llama_models.datatypes import SamplingParams, TopPSamplingStrategy from llama_models.llama3.api.datatypes import BuiltinTool from llama_stack.apis.agents import ( @@ -22,7 +23,8 @@ from llama_stack.apis.agents import ( ToolExecutionStep, Turn, ) -from llama_stack.apis.inference import CompletionMessage, SamplingParams, UserMessage + +from llama_stack.apis.inference import CompletionMessage, UserMessage from llama_stack.apis.safety import ViolationLevel from llama_stack.providers.datatypes import Api @@ -42,7 +44,9 @@ def common_params(inference_model): model=inference_model, instructions="You are a helpful assistant.", enable_session_persistence=True, - sampling_params=SamplingParams(temperature=0.7, top_p=0.95), + sampling_params=SamplingParams( + strategy=TopPSamplingStrategy(temperature=0.7, top_p=0.95) + ), input_shields=[], output_shields=[], toolgroups=[], diff --git a/llama_stack/providers/tests/inference/groq/test_groq_utils.py b/llama_stack/providers/tests/inference/groq/test_groq_utils.py index f3f263cb1..0402a772c 100644 --- a/llama_stack/providers/tests/inference/groq/test_groq_utils.py +++ b/llama_stack/providers/tests/inference/groq/test_groq_utils.py @@ -21,6 +21,7 @@ from groq.types.chat.chat_completion_message_tool_call import ( Function, ) from groq.types.shared.function_definition import FunctionDefinition +from llama_models.datatypes import GreedySamplingStrategy, TopPSamplingStrategy from llama_models.llama3.api.datatypes import ToolParamDefinition from llama_stack.apis.inference import ( ChatCompletionRequest, @@ -152,21 +153,30 @@ class TestConvertChatCompletionRequest: assert converted["max_tokens"] == 100 - def test_includes_temperature(self): + def _dummy_chat_completion_request(self): + return ChatCompletionRequest( + model="Llama-3.2-3B", + messages=[UserMessage(content="Hello World")], + ) + + def test_includes_stratgy(self): request = self._dummy_chat_completion_request() - request.sampling_params.temperature = 0.5 + request.sampling_params.strategy = TopPSamplingStrategy( + temperature=0.5, top_p=0.95 + ) converted = convert_chat_completion_request(request) assert converted["temperature"] == 0.5 + assert converted["top_p"] == 0.95 - def test_includes_top_p(self): + def test_includes_greedy_strategy(self): request = self._dummy_chat_completion_request() - request.sampling_params.top_p = 0.95 + request.sampling_params.strategy = GreedySamplingStrategy() converted = convert_chat_completion_request(request) - assert converted["top_p"] == 0.95 + assert converted["temperature"] == 0.0 def test_includes_tool_choice(self): request = self._dummy_chat_completion_request() @@ -268,12 +278,6 @@ class TestConvertChatCompletionRequest: }, ] - def _dummy_chat_completion_request(self): - return ChatCompletionRequest( - model="Llama-3.2-3B", - messages=[UserMessage(content="Hello World")], - ) - class TestConvertNonStreamChatCompletionResponse: def test_returns_response(self): @@ -409,19 +413,19 @@ class TestConvertStreamChatCompletionResponse: iter = converted.__aiter__() chunk = await iter.__anext__() assert chunk.event.event_type == ChatCompletionResponseEventType.start - assert chunk.event.delta == "Hello " + assert chunk.event.delta.text == "Hello " chunk = await iter.__anext__() assert chunk.event.event_type == ChatCompletionResponseEventType.progress - assert chunk.event.delta == "World " + assert chunk.event.delta.text == "World " chunk = await iter.__anext__() assert chunk.event.event_type == ChatCompletionResponseEventType.progress - assert chunk.event.delta == " !" + assert chunk.event.delta.text == " !" chunk = await iter.__anext__() assert chunk.event.event_type == ChatCompletionResponseEventType.complete - assert chunk.event.delta == "" + assert chunk.event.delta.text == "" assert chunk.event.stop_reason == StopReason.end_of_turn with pytest.raises(StopAsyncIteration): diff --git a/llama_stack/providers/tests/inference/test_text_inference.py b/llama_stack/providers/tests/inference/test_text_inference.py index 932ae36e6..037e99819 100644 --- a/llama_stack/providers/tests/inference/test_text_inference.py +++ b/llama_stack/providers/tests/inference/test_text_inference.py @@ -32,6 +32,7 @@ from llama_stack.apis.inference import ( UserMessage, ) from llama_stack.apis.models import Model + from .utils import group_chunks @@ -476,7 +477,7 @@ class TestInference: last = grouped[ChatCompletionResponseEventType.progress][-1] # assert last.event.stop_reason == expected_stop_reason assert last.event.delta.parse_status == ToolCallParseStatus.succeeded - assert last.event.delta.content.type == "tool_call" + assert isinstance(last.event.delta.content, ToolCall) call = last.event.delta.content assert call.tool_name == "get_weather" diff --git a/llama_stack/providers/utils/inference/openai_compat.py b/llama_stack/providers/utils/inference/openai_compat.py index 4c46954cf..694212a02 100644 --- a/llama_stack/providers/utils/inference/openai_compat.py +++ b/llama_stack/providers/utils/inference/openai_compat.py @@ -8,7 +8,13 @@ from typing import AsyncGenerator, List, Optional from llama_models.llama3.api.chat_format import ChatFormat -from llama_models.llama3.api.datatypes import SamplingParams, StopReason +from llama_models.llama3.api.datatypes import ( + GreedySamplingStrategy, + SamplingParams, + StopReason, + TopKSamplingStrategy, + TopPSamplingStrategy, +) from pydantic import BaseModel from llama_stack.apis.common.content_types import ( @@ -49,12 +55,27 @@ class OpenAICompatCompletionResponse(BaseModel): choices: List[OpenAICompatCompletionChoice] +def get_sampling_strategy_options(params: SamplingParams) -> dict: + options = {} + if isinstance(params.strategy, GreedySamplingStrategy): + options["temperature"] = 0.0 + elif isinstance(params.strategy, TopPSamplingStrategy): + options["temperature"] = params.strategy.temperature + options["top_p"] = params.strategy.top_p + elif isinstance(params.strategy, TopKSamplingStrategy): + options["top_k"] = params.strategy.top_k + else: + raise ValueError(f"Unsupported sampling strategy: {params.strategy}") + + return options + + def get_sampling_options(params: SamplingParams) -> dict: options = {} if params: - for attr in {"temperature", "top_p", "top_k", "max_tokens"}: - if getattr(params, attr): - options[attr] = getattr(params, attr) + options.update(get_sampling_strategy_options(params)) + if params.max_tokens: + options["max_tokens"] = params.max_tokens if params.repetition_penalty is not None and params.repetition_penalty != 1.0: options["repeat_penalty"] = params.repetition_penalty diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py index 0c16b6225..19a4064a0 100644 --- a/tests/client-sdk/agents/test_agents.py +++ b/tests/client-sdk/agents/test_agents.py @@ -97,9 +97,11 @@ def agent_config(llama_stack_client): model=model_id, instructions="You are a helpful assistant", sampling_params={ - "strategy": "greedy", - "temperature": 1.0, - "top_p": 0.9, + "strategy": { + "type": "greedy", + "temperature": 1.0, + "top_p": 0.9, + }, }, toolgroups=[], tool_choice="auto", From b78e6675eae4a3ae37b8518c77059c37975baa0c Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 15 Jan 2025 05:58:09 -0800 Subject: [PATCH 012/107] llama-stack version alpha -> v1 --- docs/openapi_generator/pyopenapi/generator.py | 2 +- .../strong_typing/inspection.py | 1 - docs/resources/llama-stack-spec.html | 440 +++++++++--------- docs/resources/llama-stack-spec.yaml | 327 +++++++------ llama_stack/apis/common/content_types.py | 2 + llama_stack/apis/memory_banks/memory_banks.py | 21 +- llama_stack/apis/version.py | 2 +- llama_stack/distribution/stack.py | 2 - 8 files changed, 390 insertions(+), 407 deletions(-) diff --git a/docs/openapi_generator/pyopenapi/generator.py b/docs/openapi_generator/pyopenapi/generator.py index 23465257a..25b08f071 100644 --- a/docs/openapi_generator/pyopenapi/generator.py +++ b/docs/openapi_generator/pyopenapi/generator.py @@ -537,7 +537,6 @@ class Generator: success_type_descriptions = { item: doc_string.short_description for item, doc_string in success_type_docstring.items() - if doc_string.short_description } else: # use return type as a single response type @@ -596,6 +595,7 @@ class Generator: ) responses.update(response_builder.build_response(response_options)) + assert len(responses.keys()) > 0, f"No responses found for {op.name}" if op.event_type is not None: builder = ContentBuilder(self.schema_builder) callbacks = { diff --git a/docs/openapi_generator/strong_typing/inspection.py b/docs/openapi_generator/strong_typing/inspection.py index c5e7899fa..41804f12c 100644 --- a/docs/openapi_generator/strong_typing/inspection.py +++ b/docs/openapi_generator/strong_typing/inspection.py @@ -342,7 +342,6 @@ def is_type_union(typ: object) -> bool: "True if the type annotation corresponds to a union type (e.g. `Union[T1,T2,T3]`)." typ = unwrap_annotated_type(typ) - if _is_union_like(typ): args = typing.get_args(typ) return len(args) > 2 or type(None) not in args diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index ad210a502..3f74a79cf 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -20,7 +20,7 @@ "openapi": "3.1.0", "info": { "title": "Llama Stack Specification", - "version": "alpha", + "version": "v1", "description": "This is the specification of the Llama Stack that provides\n a set of endpoints and their corresponding interfaces that are tailored to\n best leverage Llama Models." }, "servers": [ @@ -29,7 +29,7 @@ } ], "paths": { - "/alpha/datasetio/append-rows": { + "/v1/datasetio/append-rows": { "post": { "responses": { "200": { @@ -71,7 +71,7 @@ } } }, - "/alpha/batch-inference/chat-completion": { + "/v1/batch-inference/chat-completion": { "post": { "responses": { "200": { @@ -120,7 +120,7 @@ } } }, - "/alpha/batch-inference/completion": { + "/v1/batch-inference/completion": { "post": { "responses": { "200": { @@ -169,7 +169,7 @@ } } }, - "/alpha/post-training/job/cancel": { + "/v1/post-training/job/cancel": { "post": { "responses": { "200": { @@ -211,7 +211,7 @@ } } }, - "/alpha/inference/chat-completion": { + "/v1/inference/chat-completion": { "post": { "responses": { "200": { @@ -267,7 +267,7 @@ } } }, - "/alpha/inference/completion": { + "/v1/inference/completion": { "post": { "responses": { "200": { @@ -323,7 +323,7 @@ } } }, - "/alpha/agents/create": { + "/v1/agents/create": { "post": { "responses": { "200": { @@ -372,7 +372,7 @@ } } }, - "/alpha/agents/session/create": { + "/v1/agents/session/create": { "post": { "responses": { "200": { @@ -421,7 +421,7 @@ } } }, - "/alpha/agents/turn/create": { + "/v1/agents/turn/create": { "post": { "responses": { "200": { @@ -477,7 +477,7 @@ } } }, - "/alpha/agents/delete": { + "/v1/agents/delete": { "post": { "responses": { "200": { @@ -519,7 +519,7 @@ } } }, - "/alpha/agents/session/delete": { + "/v1/agents/session/delete": { "post": { "responses": { "200": { @@ -561,7 +561,7 @@ } } }, - "/alpha/inference/embeddings": { + "/v1/inference/embeddings": { "post": { "responses": { "200": { @@ -610,7 +610,7 @@ } } }, - "/alpha/eval/evaluate-rows": { + "/v1/eval/evaluate-rows": { "post": { "responses": { "200": { @@ -659,7 +659,7 @@ } } }, - "/alpha/agents/session/get": { + "/v1/agents/session/get": { "post": { "responses": { "200": { @@ -724,7 +724,7 @@ } } }, - "/alpha/agents/step/get": { + "/v1/agents/step/get": { "get": { "responses": { "200": { @@ -795,7 +795,7 @@ ] } }, - "/alpha/agents/turn/get": { + "/v1/agents/turn/get": { "get": { "responses": { "200": { @@ -858,7 +858,7 @@ ] } }, - "/alpha/datasets/get": { + "/v1/datasets/get": { "get": { "responses": { "200": { @@ -912,7 +912,7 @@ ] } }, - "/alpha/eval-tasks/get": { + "/v1/eval-tasks/get": { "get": { "responses": { "200": { @@ -966,7 +966,7 @@ ] } }, - "/alpha/memory-banks/get": { + "/v1/memory-banks/get": { "get": { "responses": { "200": { @@ -976,20 +976,7 @@ "schema": { "oneOf": [ { - "oneOf": [ - { - "$ref": "#/components/schemas/VectorMemoryBank" - }, - { - "$ref": "#/components/schemas/KeyValueMemoryBank" - }, - { - "$ref": "#/components/schemas/KeywordMemoryBank" - }, - { - "$ref": "#/components/schemas/GraphMemoryBank" - } - ] + "$ref": "#/components/schemas/MemoryBank" }, { "type": "null" @@ -1033,7 +1020,7 @@ ] } }, - "/alpha/models/get": { + "/v1/models/get": { "get": { "responses": { "200": { @@ -1087,7 +1074,7 @@ ] } }, - "/alpha/datasetio/get-rows-paginated": { + "/v1/datasetio/get-rows-paginated": { "get": { "responses": { "200": { @@ -1158,7 +1145,7 @@ ] } }, - "/alpha/scoring-functions/get": { + "/v1/scoring-functions/get": { "get": { "responses": { "200": { @@ -1212,7 +1199,7 @@ ] } }, - "/alpha/shields/get": { + "/v1/shields/get": { "get": { "responses": { "200": { @@ -1266,7 +1253,7 @@ ] } }, - "/alpha/telemetry/get-span-tree": { + "/v1/telemetry/get-span-tree": { "post": { "responses": { "200": { @@ -1334,7 +1321,7 @@ } } }, - "/alpha/tools/get": { + "/v1/tools/get": { "get": { "responses": { "200": { @@ -1381,7 +1368,7 @@ ] } }, - "/alpha/toolgroups/get": { + "/v1/toolgroups/get": { "get": { "responses": { "200": { @@ -1428,7 +1415,7 @@ ] } }, - "/alpha/post-training/job/artifacts": { + "/v1/post-training/job/artifacts": { "get": { "responses": { "200": { @@ -1482,7 +1469,7 @@ ] } }, - "/alpha/post-training/job/status": { + "/v1/post-training/job/status": { "get": { "responses": { "200": { @@ -1536,7 +1523,7 @@ ] } }, - "/alpha/post-training/jobs": { + "/v1/post-training/jobs": { "get": { "responses": { "200": { @@ -1575,7 +1562,7 @@ ] } }, - "/alpha/health": { + "/v1/health": { "get": { "responses": { "200": { @@ -1614,7 +1601,7 @@ ] } }, - "/alpha/memory/insert": { + "/v1/memory/insert": { "post": { "responses": { "200": { @@ -1656,7 +1643,7 @@ } } }, - "/alpha/tool-runtime/invoke": { + "/v1/tool-runtime/invoke": { "post": { "responses": { "200": { @@ -1706,7 +1693,7 @@ } } }, - "/alpha/eval/job/cancel": { + "/v1/eval/job/cancel": { "post": { "responses": { "200": { @@ -1748,7 +1735,7 @@ } } }, - "/alpha/eval/job/result": { + "/v1/eval/job/result": { "get": { "responses": { "200": { @@ -1803,7 +1790,7 @@ ] } }, - "/alpha/eval/job/status": { + "/v1/eval/job/status": { "get": { "responses": { "200": { @@ -1865,7 +1852,7 @@ ] } }, - "/alpha/datasets/list": { + "/v1/datasets/list": { "get": { "responses": { "200": { @@ -1904,7 +1891,7 @@ ] } }, - "/alpha/eval-tasks/list": { + "/v1/eval-tasks/list": { "get": { "responses": { "200": { @@ -1943,7 +1930,7 @@ ] } }, - "/alpha/memory-banks/list": { + "/v1/memory-banks/list": { "get": { "responses": { "200": { @@ -1951,20 +1938,7 @@ "content": { "application/jsonl": { "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/VectorMemoryBank" - }, - { - "$ref": "#/components/schemas/KeyValueMemoryBank" - }, - { - "$ref": "#/components/schemas/KeywordMemoryBank" - }, - { - "$ref": "#/components/schemas/GraphMemoryBank" - } - ] + "$ref": "#/components/schemas/MemoryBank" } } } @@ -1995,7 +1969,7 @@ ] } }, - "/alpha/models/list": { + "/v1/models/list": { "get": { "responses": { "200": { @@ -2034,7 +2008,7 @@ ] } }, - "/alpha/providers/list": { + "/v1/providers/list": { "get": { "responses": { "200": { @@ -2076,7 +2050,7 @@ ] } }, - "/alpha/routes/list": { + "/v1/routes/list": { "get": { "responses": { "200": { @@ -2121,7 +2095,7 @@ ] } }, - "/alpha/tool-runtime/list-tools": { + "/v1/tool-runtime/list-tools": { "post": { "responses": { "200": { @@ -2178,7 +2152,7 @@ } } }, - "/alpha/scoring-functions/list": { + "/v1/scoring-functions/list": { "get": { "responses": { "200": { @@ -2217,7 +2191,7 @@ ] } }, - "/alpha/shields/list": { + "/v1/shields/list": { "get": { "responses": { "200": { @@ -2256,7 +2230,7 @@ ] } }, - "/alpha/toolgroups/list": { + "/v1/toolgroups/list": { "get": { "responses": { "200": { @@ -2296,7 +2270,7 @@ ] } }, - "/alpha/tools/list": { + "/v1/tools/list": { "get": { "responses": { "200": { @@ -2344,7 +2318,7 @@ ] } }, - "/alpha/telemetry/log-event": { + "/v1/telemetry/log-event": { "post": { "responses": { "200": { @@ -2386,7 +2360,7 @@ } } }, - "/alpha/post-training/preference-optimize": { + "/v1/post-training/preference-optimize": { "post": { "responses": { "200": { @@ -2435,7 +2409,7 @@ } } }, - "/alpha/memory/query": { + "/v1/memory/query": { "post": { "responses": { "200": { @@ -2484,7 +2458,7 @@ } } }, - "/alpha/telemetry/query-spans": { + "/v1/telemetry/query-spans": { "post": { "responses": { "200": { @@ -2533,7 +2507,7 @@ } } }, - "/alpha/telemetry/query-traces": { + "/v1/telemetry/query-traces": { "post": { "responses": { "200": { @@ -2582,7 +2556,7 @@ } } }, - "/alpha/datasets/register": { + "/v1/datasets/register": { "post": { "responses": { "200": { @@ -2624,7 +2598,7 @@ } } }, - "/alpha/eval-tasks/register": { + "/v1/eval-tasks/register": { "post": { "responses": { "200": { @@ -2666,9 +2640,33 @@ } } }, - "/alpha/memory-banks/register": { + "/v1/memory-banks/register": { "post": { - "responses": {}, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VectorMemoryBank" + }, + { + "$ref": "#/components/schemas/KeyValueMemoryBank" + }, + { + "$ref": "#/components/schemas/KeywordMemoryBank" + }, + { + "$ref": "#/components/schemas/GraphMemoryBank" + } + ] + } + } + } + } + }, "tags": [ "MemoryBanks" ], @@ -2704,7 +2702,7 @@ } } }, - "/alpha/models/register": { + "/v1/models/register": { "post": { "responses": { "200": { @@ -2753,7 +2751,7 @@ } } }, - "/alpha/scoring-functions/register": { + "/v1/scoring-functions/register": { "post": { "responses": { "200": { @@ -2795,7 +2793,7 @@ } } }, - "/alpha/shields/register": { + "/v1/shields/register": { "post": { "responses": { "200": { @@ -2844,7 +2842,7 @@ } } }, - "/alpha/toolgroups/register": { + "/v1/toolgroups/register": { "post": { "responses": { "200": { @@ -2887,7 +2885,7 @@ } } }, - "/alpha/eval/run-eval": { + "/v1/eval/run-eval": { "post": { "responses": { "200": { @@ -2936,7 +2934,7 @@ } } }, - "/alpha/safety/run-shield": { + "/v1/safety/run-shield": { "post": { "responses": { "200": { @@ -2985,7 +2983,7 @@ } } }, - "/alpha/telemetry/save-spans-to-dataset": { + "/v1/telemetry/save-spans-to-dataset": { "post": { "responses": { "200": { @@ -3027,7 +3025,7 @@ } } }, - "/alpha/scoring/score": { + "/v1/scoring/score": { "post": { "responses": { "200": { @@ -3076,7 +3074,7 @@ } } }, - "/alpha/scoring/score-batch": { + "/v1/scoring/score-batch": { "post": { "responses": { "200": { @@ -3125,7 +3123,7 @@ } } }, - "/alpha/post-training/supervised-fine-tune": { + "/v1/post-training/supervised-fine-tune": { "post": { "responses": { "200": { @@ -3174,7 +3172,7 @@ } } }, - "/alpha/synthetic-data-generation/generate": { + "/v1/synthetic-data-generation/generate": { "post": { "responses": { "200": { @@ -3223,7 +3221,7 @@ } } }, - "/alpha/datasets/unregister": { + "/v1/datasets/unregister": { "post": { "responses": { "200": { @@ -3265,7 +3263,7 @@ } } }, - "/alpha/memory-banks/unregister": { + "/v1/memory-banks/unregister": { "post": { "responses": { "200": { @@ -3307,7 +3305,7 @@ } } }, - "/alpha/models/unregister": { + "/v1/models/unregister": { "post": { "responses": { "200": { @@ -3349,7 +3347,7 @@ } } }, - "/alpha/toolgroups/unregister": { + "/v1/toolgroups/unregister": { "post": { "responses": { "200": { @@ -3392,7 +3390,7 @@ } } }, - "/alpha/version": { + "/v1/version": { "get": { "responses": { "200": { @@ -3514,20 +3512,6 @@ "tool_calls" ] }, - "GreedySamplingStrategy": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "greedy", - "default": "greedy" - } - }, - "additionalProperties": false, - "required": [ - "type" - ] - }, "ImageContentItem": { "type": "object", "properties": { @@ -3595,17 +3579,20 @@ "type": "object", "properties": { "strategy": { - "oneOf": [ - { - "$ref": "#/components/schemas/GreedySamplingStrategy" - }, - { - "$ref": "#/components/schemas/TopPSamplingStrategy" - }, - { - "$ref": "#/components/schemas/TopKSamplingStrategy" - } - ] + "$ref": "#/components/schemas/SamplingStrategy", + "default": "greedy" + }, + "temperature": { + "type": "number", + "default": 0.0 + }, + "top_p": { + "type": "number", + "default": 0.95 + }, + "top_k": { + "type": "integer", + "default": 0 }, "max_tokens": { "type": "integer", @@ -3621,6 +3608,14 @@ "strategy" ] }, + "SamplingStrategy": { + "type": "string", + "enum": [ + "greedy", + "top_p", + "top_k" + ] + }, "StopReason": { "type": "string", "enum": [ @@ -3874,45 +3869,6 @@ "content" ] }, - "TopKSamplingStrategy": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "top_k", - "default": "top_k" - }, - "top_k": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "type", - "top_k" - ] - }, - "TopPSamplingStrategy": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "top_p", - "default": "top_p" - }, - "temperature": { - "type": "number" - }, - "top_p": { - "type": "number", - "default": 0.95 - } - }, - "additionalProperties": false, - "required": [ - "type" - ] - }, "URL": { "type": "object", "properties": { @@ -4270,47 +4226,53 @@ "ContentDelta": { "oneOf": [ { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "text", - "default": "text" - }, - "text": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type", - "text" - ] + "$ref": "#/components/schemas/TextDelta" }, { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "image", - "default": "image" - }, - "data": { - "type": "string", - "contentEncoding": "base64" - } - }, - "additionalProperties": false, - "required": [ - "type", - "data" - ] + "$ref": "#/components/schemas/ImageDelta" }, { "$ref": "#/components/schemas/ToolCallDelta" } ] }, + "ImageDelta": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "image", + "default": "image" + }, + "data": { + "type": "string", + "contentEncoding": "base64" + } + }, + "additionalProperties": false, + "required": [ + "type", + "data" + ] + }, + "TextDelta": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "text", + "default": "text" + }, + "text": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "type", + "text" + ] + }, "TokenLogProbs": { "type": "object", "properties": { @@ -5847,6 +5809,22 @@ "memory_bank_type" ] }, + "MemoryBank": { + "oneOf": [ + { + "$ref": "#/components/schemas/VectorMemoryBank" + }, + { + "$ref": "#/components/schemas/KeyValueMemoryBank" + }, + { + "$ref": "#/components/schemas/KeywordMemoryBank" + }, + { + "$ref": "#/components/schemas/GraphMemoryBank" + } + ] + }, "Session": { "type": "object", "properties": { @@ -5867,20 +5845,7 @@ "format": "date-time" }, "memory_bank": { - "oneOf": [ - { - "$ref": "#/components/schemas/VectorMemoryBank" - }, - { - "$ref": "#/components/schemas/KeyValueMemoryBank" - }, - { - "$ref": "#/components/schemas/KeywordMemoryBank" - }, - { - "$ref": "#/components/schemas/GraphMemoryBank" - } - ] + "$ref": "#/components/schemas/MemoryBank" } }, "additionalProperties": false, @@ -7303,6 +7268,9 @@ "shuffle": { "type": "boolean" }, + "data_format": { + "$ref": "#/components/schemas/DatasetFormat" + }, "validation_dataset_id": { "type": "string" }, @@ -7319,7 +7287,15 @@ "required": [ "dataset_id", "batch_size", - "shuffle" + "shuffle", + "data_format" + ] + }, + "DatasetFormat": { + "type": "string", + "enum": [ + "instruct", + "dialog" ] }, "EfficiencyConfig": { @@ -8869,6 +8845,10 @@ "name": "Dataset", "description": "" }, + { + "name": "DatasetFormat", + "description": "" + }, { "name": "DatasetIO" }, @@ -8929,10 +8909,6 @@ "name": "GraphMemoryBankParams", "description": "" }, - { - "name": "GreedySamplingStrategy", - "description": "" - }, { "name": "HealthInfo", "description": "" @@ -8941,6 +8917,10 @@ "name": "ImageContentItem", "description": "" }, + { + "name": "ImageDelta", + "description": "" + }, { "name": "Inference" }, @@ -9018,6 +8998,10 @@ { "name": "Memory" }, + { + "name": "MemoryBank", + "description": "" + }, { "name": "MemoryBankDocument", "description": "" @@ -9182,6 +9166,10 @@ "name": "SamplingParams", "description": "" }, + { + "name": "SamplingStrategy", + "description": "" + }, { "name": "SaveSpansToDatasetRequest", "description": "" @@ -9285,6 +9273,10 @@ "name": "TextContentItem", "description": "" }, + { + "name": "TextDelta", + "description": "" + }, { "name": "TokenLogProbs", "description": "" @@ -9359,14 +9351,6 @@ { "name": "ToolRuntime" }, - { - "name": "TopKSamplingStrategy", - "description": "" - }, - { - "name": "TopPSamplingStrategy", - "description": "" - }, { "name": "Trace", "description": "" @@ -9494,6 +9478,7 @@ "DPOAlignmentConfig", "DataConfig", "Dataset", + "DatasetFormat", "DeleteAgentsRequest", "DeleteAgentsSessionRequest", "EfficiencyConfig", @@ -9506,9 +9491,9 @@ "GetSpanTreeRequest", "GraphMemoryBank", "GraphMemoryBankParams", - "GreedySamplingStrategy", "HealthInfo", "ImageContentItem", + "ImageDelta", "InferenceStep", "InsertDocumentsRequest", "InterleavedContent", @@ -9526,6 +9511,7 @@ "LogEventRequest", "LogSeverity", "LoraFinetuningConfig", + "MemoryBank", "MemoryBankDocument", "MemoryRetrievalStep", "Message", @@ -9564,6 +9550,7 @@ "RunShieldResponse", "SafetyViolation", "SamplingParams", + "SamplingStrategy", "SaveSpansToDatasetRequest", "ScoreBatchRequest", "ScoreBatchResponse", @@ -9586,6 +9573,7 @@ "SyntheticDataGenerationResponse", "SystemMessage", "TextContentItem", + "TextDelta", "TokenLogProbs", "Tool", "ToolCall", @@ -9603,8 +9591,6 @@ "ToolPromptFormat", "ToolResponse", "ToolResponseMessage", - "TopKSamplingStrategy", - "TopPSamplingStrategy", "Trace", "TrainingConfig", "Turn", diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index 8c885b7e5..2afb8e375 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -570,31 +570,8 @@ components: type: object ContentDelta: oneOf: - - additionalProperties: false - properties: - text: - type: string - type: - const: text - default: text - type: string - required: - - type - - text - type: object - - additionalProperties: false - properties: - data: - contentEncoding: base64 - type: string - type: - const: image - default: image - type: string - required: - - type - - data - type: object + - $ref: '#/components/schemas/TextDelta' + - $ref: '#/components/schemas/ImageDelta' - $ref: '#/components/schemas/ToolCallDelta' CreateAgentRequest: additionalProperties: false @@ -680,6 +657,8 @@ components: properties: batch_size: type: integer + data_format: + $ref: '#/components/schemas/DatasetFormat' dataset_id: type: string packed: @@ -696,6 +675,7 @@ components: - dataset_id - batch_size - shuffle + - data_format type: object Dataset: additionalProperties: false @@ -735,6 +715,11 @@ components: - url - metadata type: object + DatasetFormat: + enum: + - instruct + - dialog + type: string DeleteAgentsRequest: additionalProperties: false properties: @@ -937,16 +922,6 @@ components: required: - memory_bank_type type: object - GreedySamplingStrategy: - additionalProperties: false - properties: - type: - const: greedy - default: greedy - type: string - required: - - type - type: object HealthInfo: additionalProperties: false properties: @@ -970,6 +945,20 @@ components: required: - type type: object + ImageDelta: + additionalProperties: false + properties: + data: + contentEncoding: base64 + type: string + type: + const: image + default: image + type: string + required: + - type + - data + type: object InferenceStep: additionalProperties: false properties: @@ -1219,6 +1208,12 @@ components: - rank - alpha type: object + MemoryBank: + oneOf: + - $ref: '#/components/schemas/VectorMemoryBank' + - $ref: '#/components/schemas/KeyValueMemoryBank' + - $ref: '#/components/schemas/KeywordMemoryBank' + - $ref: '#/components/schemas/GraphMemoryBank' MemoryBankDocument: additionalProperties: false properties: @@ -2074,13 +2069,26 @@ components: default: 1.0 type: number strategy: - oneOf: - - $ref: '#/components/schemas/GreedySamplingStrategy' - - $ref: '#/components/schemas/TopPSamplingStrategy' - - $ref: '#/components/schemas/TopKSamplingStrategy' + $ref: '#/components/schemas/SamplingStrategy' + default: greedy + temperature: + default: 0.0 + type: number + top_k: + default: 0 + type: integer + top_p: + default: 0.95 + type: number required: - strategy type: object + SamplingStrategy: + enum: + - greedy + - top_p + - top_k + type: string SaveSpansToDatasetRequest: additionalProperties: false properties: @@ -2245,11 +2253,7 @@ components: additionalProperties: false properties: memory_bank: - oneOf: - - $ref: '#/components/schemas/VectorMemoryBank' - - $ref: '#/components/schemas/KeyValueMemoryBank' - - $ref: '#/components/schemas/KeywordMemoryBank' - - $ref: '#/components/schemas/GraphMemoryBank' + $ref: '#/components/schemas/MemoryBank' session_id: type: string session_name: @@ -2585,6 +2589,19 @@ components: - type - text type: object + TextDelta: + additionalProperties: false + properties: + text: + type: string + type: + const: text + default: text + type: string + required: + - type + - text + type: object TokenLogProbs: additionalProperties: false properties: @@ -2928,34 +2945,6 @@ components: - tool_name - content type: object - TopKSamplingStrategy: - additionalProperties: false - properties: - top_k: - type: integer - type: - const: top_k - default: top_k - type: string - required: - - type - - top_k - type: object - TopPSamplingStrategy: - additionalProperties: false - properties: - temperature: - type: number - top_p: - default: 0.95 - type: number - type: - const: top_p - default: top_p - type: string - required: - - type - type: object Trace: additionalProperties: false properties: @@ -3223,11 +3212,11 @@ info: \ a set of endpoints and their corresponding interfaces that are tailored\ \ to\n best leverage Llama Models." title: Llama Stack Specification - version: alpha + version: v1 jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema openapi: 3.1.0 paths: - /alpha/agents/create: + /v1/agents/create: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3259,7 +3248,7 @@ paths: description: OK tags: - Agents - /alpha/agents/delete: + /v1/agents/delete: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3287,7 +3276,7 @@ paths: description: OK tags: - Agents - /alpha/agents/session/create: + /v1/agents/session/create: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3319,7 +3308,7 @@ paths: description: OK tags: - Agents - /alpha/agents/session/delete: + /v1/agents/session/delete: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3347,7 +3336,7 @@ paths: description: OK tags: - Agents - /alpha/agents/session/get: + /v1/agents/session/get: post: parameters: - in: query @@ -3389,7 +3378,7 @@ paths: description: OK tags: - Agents - /alpha/agents/step/get: + /v1/agents/step/get: get: parameters: - in: query @@ -3435,7 +3424,7 @@ paths: description: OK tags: - Agents - /alpha/agents/turn/create: + /v1/agents/turn/create: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3470,7 +3459,7 @@ paths: streamed agent turn completion response. tags: - Agents - /alpha/agents/turn/get: + /v1/agents/turn/get: get: parameters: - in: query @@ -3511,7 +3500,7 @@ paths: description: OK tags: - Agents - /alpha/batch-inference/chat-completion: + /v1/batch-inference/chat-completion: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3543,7 +3532,7 @@ paths: description: OK tags: - BatchInference (Coming Soon) - /alpha/batch-inference/completion: + /v1/batch-inference/completion: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3575,7 +3564,7 @@ paths: description: OK tags: - BatchInference (Coming Soon) - /alpha/datasetio/append-rows: + /v1/datasetio/append-rows: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3603,7 +3592,7 @@ paths: description: OK tags: - DatasetIO - /alpha/datasetio/get-rows-paginated: + /v1/datasetio/get-rows-paginated: get: parameters: - in: query @@ -3649,7 +3638,7 @@ paths: description: OK tags: - DatasetIO - /alpha/datasets/get: + /v1/datasets/get: get: parameters: - in: query @@ -3682,7 +3671,7 @@ paths: description: OK tags: - Datasets - /alpha/datasets/list: + /v1/datasets/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3708,7 +3697,7 @@ paths: description: OK tags: - Datasets - /alpha/datasets/register: + /v1/datasets/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3736,7 +3725,7 @@ paths: description: OK tags: - Datasets - /alpha/datasets/unregister: + /v1/datasets/unregister: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3764,7 +3753,7 @@ paths: description: OK tags: - Datasets - /alpha/eval-tasks/get: + /v1/eval-tasks/get: get: parameters: - in: query @@ -3797,7 +3786,7 @@ paths: description: OK tags: - EvalTasks - /alpha/eval-tasks/list: + /v1/eval-tasks/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3823,7 +3812,7 @@ paths: description: OK tags: - EvalTasks - /alpha/eval-tasks/register: + /v1/eval-tasks/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3851,7 +3840,7 @@ paths: description: OK tags: - EvalTasks - /alpha/eval/evaluate-rows: + /v1/eval/evaluate-rows: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3883,7 +3872,7 @@ paths: description: OK tags: - Eval - /alpha/eval/job/cancel: + /v1/eval/job/cancel: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3911,7 +3900,7 @@ paths: description: OK tags: - Eval - /alpha/eval/job/result: + /v1/eval/job/result: get: parameters: - in: query @@ -3947,7 +3936,7 @@ paths: description: OK tags: - Eval - /alpha/eval/job/status: + /v1/eval/job/status: get: parameters: - in: query @@ -3985,7 +3974,7 @@ paths: description: OK tags: - Eval - /alpha/eval/run-eval: + /v1/eval/run-eval: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4017,7 +4006,7 @@ paths: description: OK tags: - Eval - /alpha/health: + /v1/health: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4043,7 +4032,7 @@ paths: description: OK tags: - Inspect - /alpha/inference/chat-completion: + /v1/inference/chat-completion: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4077,7 +4066,7 @@ paths: description: Chat completion response. **OR** SSE-stream of these events. tags: - Inference - /alpha/inference/completion: + /v1/inference/completion: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4111,7 +4100,7 @@ paths: description: Completion response. **OR** streamed completion response. tags: - Inference - /alpha/inference/embeddings: + /v1/inference/embeddings: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4143,7 +4132,7 @@ paths: description: OK tags: - Inference - /alpha/memory-banks/get: + /v1/memory-banks/get: get: parameters: - in: query @@ -4171,16 +4160,12 @@ paths: application/json: schema: oneOf: - - oneOf: - - $ref: '#/components/schemas/VectorMemoryBank' - - $ref: '#/components/schemas/KeyValueMemoryBank' - - $ref: '#/components/schemas/KeywordMemoryBank' - - $ref: '#/components/schemas/GraphMemoryBank' + - $ref: '#/components/schemas/MemoryBank' - type: 'null' description: OK tags: - MemoryBanks - /alpha/memory-banks/list: + /v1/memory-banks/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4202,15 +4187,11 @@ paths: content: application/jsonl: schema: - oneOf: - - $ref: '#/components/schemas/VectorMemoryBank' - - $ref: '#/components/schemas/KeyValueMemoryBank' - - $ref: '#/components/schemas/KeywordMemoryBank' - - $ref: '#/components/schemas/GraphMemoryBank' + $ref: '#/components/schemas/MemoryBank' description: OK tags: - MemoryBanks - /alpha/memory-banks/register: + /v1/memory-banks/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4233,10 +4214,20 @@ paths: schema: $ref: '#/components/schemas/RegisterMemoryBankRequest' required: true - responses: {} + responses: + '200': + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/VectorMemoryBank' + - $ref: '#/components/schemas/KeyValueMemoryBank' + - $ref: '#/components/schemas/KeywordMemoryBank' + - $ref: '#/components/schemas/GraphMemoryBank' + description: '' tags: - MemoryBanks - /alpha/memory-banks/unregister: + /v1/memory-banks/unregister: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4264,7 +4255,7 @@ paths: description: OK tags: - MemoryBanks - /alpha/memory/insert: + /v1/memory/insert: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4292,7 +4283,7 @@ paths: description: OK tags: - Memory - /alpha/memory/query: + /v1/memory/query: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4324,7 +4315,7 @@ paths: description: OK tags: - Memory - /alpha/models/get: + /v1/models/get: get: parameters: - in: query @@ -4357,7 +4348,7 @@ paths: description: OK tags: - Models - /alpha/models/list: + /v1/models/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4383,7 +4374,7 @@ paths: description: OK tags: - Models - /alpha/models/register: + /v1/models/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4415,7 +4406,7 @@ paths: description: OK tags: - Models - /alpha/models/unregister: + /v1/models/unregister: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4443,7 +4434,7 @@ paths: description: OK tags: - Models - /alpha/post-training/job/artifacts: + /v1/post-training/job/artifacts: get: parameters: - in: query @@ -4476,7 +4467,7 @@ paths: description: OK tags: - PostTraining (Coming Soon) - /alpha/post-training/job/cancel: + /v1/post-training/job/cancel: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4504,7 +4495,7 @@ paths: description: OK tags: - PostTraining (Coming Soon) - /alpha/post-training/job/status: + /v1/post-training/job/status: get: parameters: - in: query @@ -4537,7 +4528,7 @@ paths: description: OK tags: - PostTraining (Coming Soon) - /alpha/post-training/jobs: + /v1/post-training/jobs: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4563,7 +4554,7 @@ paths: description: OK tags: - PostTraining (Coming Soon) - /alpha/post-training/preference-optimize: + /v1/post-training/preference-optimize: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4595,7 +4586,7 @@ paths: description: OK tags: - PostTraining (Coming Soon) - /alpha/post-training/supervised-fine-tune: + /v1/post-training/supervised-fine-tune: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4627,7 +4618,7 @@ paths: description: OK tags: - PostTraining (Coming Soon) - /alpha/providers/list: + /v1/providers/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4655,7 +4646,7 @@ paths: description: OK tags: - Inspect - /alpha/routes/list: + /v1/routes/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4685,7 +4676,7 @@ paths: description: OK tags: - Inspect - /alpha/safety/run-shield: + /v1/safety/run-shield: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4717,7 +4708,7 @@ paths: description: OK tags: - Safety - /alpha/scoring-functions/get: + /v1/scoring-functions/get: get: parameters: - in: query @@ -4750,7 +4741,7 @@ paths: description: OK tags: - ScoringFunctions - /alpha/scoring-functions/list: + /v1/scoring-functions/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4776,7 +4767,7 @@ paths: description: OK tags: - ScoringFunctions - /alpha/scoring-functions/register: + /v1/scoring-functions/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4804,7 +4795,7 @@ paths: description: OK tags: - ScoringFunctions - /alpha/scoring/score: + /v1/scoring/score: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4836,7 +4827,7 @@ paths: description: OK tags: - Scoring - /alpha/scoring/score-batch: + /v1/scoring/score-batch: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4868,7 +4859,7 @@ paths: description: OK tags: - Scoring - /alpha/shields/get: + /v1/shields/get: get: parameters: - in: query @@ -4901,7 +4892,7 @@ paths: description: OK tags: - Shields - /alpha/shields/list: + /v1/shields/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4927,7 +4918,7 @@ paths: description: OK tags: - Shields - /alpha/shields/register: + /v1/shields/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4959,7 +4950,7 @@ paths: description: OK tags: - Shields - /alpha/synthetic-data-generation/generate: + /v1/synthetic-data-generation/generate: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4991,7 +4982,7 @@ paths: description: OK tags: - SyntheticDataGeneration (Coming Soon) - /alpha/telemetry/get-span-tree: + /v1/telemetry/get-span-tree: post: parameters: - in: query @@ -5035,7 +5026,7 @@ paths: description: OK tags: - Telemetry - /alpha/telemetry/log-event: + /v1/telemetry/log-event: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5063,7 +5054,7 @@ paths: description: OK tags: - Telemetry - /alpha/telemetry/query-spans: + /v1/telemetry/query-spans: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5095,7 +5086,7 @@ paths: description: OK tags: - Telemetry - /alpha/telemetry/query-traces: + /v1/telemetry/query-traces: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5127,7 +5118,7 @@ paths: description: OK tags: - Telemetry - /alpha/telemetry/save-spans-to-dataset: + /v1/telemetry/save-spans-to-dataset: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5155,7 +5146,7 @@ paths: description: OK tags: - Telemetry - /alpha/tool-runtime/invoke: + /v1/tool-runtime/invoke: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5188,7 +5179,7 @@ paths: summary: Run a tool with the given arguments tags: - ToolRuntime - /alpha/tool-runtime/list-tools: + /v1/tool-runtime/list-tools: post: parameters: - in: query @@ -5225,7 +5216,7 @@ paths: description: OK tags: - ToolRuntime - /alpha/toolgroups/get: + /v1/toolgroups/get: get: parameters: - in: query @@ -5256,7 +5247,7 @@ paths: description: OK tags: - ToolGroups - /alpha/toolgroups/list: + /v1/toolgroups/list: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5283,7 +5274,7 @@ paths: summary: List tool groups with optional provider tags: - ToolGroups - /alpha/toolgroups/register: + /v1/toolgroups/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5312,7 +5303,7 @@ paths: summary: Register a tool group tags: - ToolGroups - /alpha/toolgroups/unregister: + /v1/toolgroups/unregister: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5341,7 +5332,7 @@ paths: summary: Unregister a tool group tags: - ToolGroups - /alpha/tools/get: + /v1/tools/get: get: parameters: - in: query @@ -5372,7 +5363,7 @@ paths: description: OK tags: - ToolGroups - /alpha/tools/list: + /v1/tools/list: get: parameters: - in: query @@ -5404,7 +5395,7 @@ paths: summary: List tools with optional tool group tags: - ToolGroups - /alpha/version: + /v1/version: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5573,6 +5564,8 @@ tags: name: DataConfig - description: name: Dataset +- description: + name: DatasetFormat - name: DatasetIO - name: Datasets - description: name: GraphMemoryBankParams -- description: - name: GreedySamplingStrategy - description: name: HealthInfo - description: name: ImageContentItem +- description: + name: ImageDelta - name: Inference - description: name: InferenceStep @@ -5670,6 +5662,8 @@ tags: /> name: LoraFinetuningConfig - name: Memory +- description: + name: MemoryBank - description: name: MemoryBankDocument @@ -5781,6 +5775,9 @@ tags: name: SafetyViolation - description: name: SamplingParams +- description: + name: SamplingStrategy - description: name: SaveSpansToDatasetRequest @@ -5849,6 +5846,8 @@ tags: - description: name: TextContentItem +- description: + name: TextDelta - description: name: TokenLogProbs - description: @@ -5899,12 +5898,6 @@ tags: /> name: ToolResponseMessage - name: ToolRuntime -- description: - name: TopKSamplingStrategy -- description: - name: TopPSamplingStrategy - description: name: Trace - description: @@ -6009,6 +6002,7 @@ x-tagGroups: - DPOAlignmentConfig - DataConfig - Dataset + - DatasetFormat - DeleteAgentsRequest - DeleteAgentsSessionRequest - EfficiencyConfig @@ -6021,9 +6015,9 @@ x-tagGroups: - GetSpanTreeRequest - GraphMemoryBank - GraphMemoryBankParams - - GreedySamplingStrategy - HealthInfo - ImageContentItem + - ImageDelta - InferenceStep - InsertDocumentsRequest - InterleavedContent @@ -6041,6 +6035,7 @@ x-tagGroups: - LogEventRequest - LogSeverity - LoraFinetuningConfig + - MemoryBank - MemoryBankDocument - MemoryRetrievalStep - Message @@ -6079,6 +6074,7 @@ x-tagGroups: - RunShieldResponse - SafetyViolation - SamplingParams + - SamplingStrategy - SaveSpansToDatasetRequest - ScoreBatchRequest - ScoreBatchResponse @@ -6101,6 +6097,7 @@ x-tagGroups: - SyntheticDataGenerationResponse - SystemMessage - TextContentItem + - TextDelta - TokenLogProbs - Tool - ToolCall @@ -6118,8 +6115,6 @@ x-tagGroups: - ToolPromptFormat - ToolResponse - ToolResponseMessage - - TopKSamplingStrategy - - TopPSamplingStrategy - Trace - TrainingConfig - Turn diff --git a/llama_stack/apis/common/content_types.py b/llama_stack/apis/common/content_types.py index 3b61fa243..b845d09dd 100644 --- a/llama_stack/apis/common/content_types.py +++ b/llama_stack/apis/common/content_types.py @@ -64,11 +64,13 @@ InterleavedContent = register_schema( ) +@json_schema_type class TextDelta(BaseModel): type: Literal["text"] = "text" text: str +@json_schema_type class ImageDelta(BaseModel): type: Literal["image"] = "image" data: bytes diff --git a/llama_stack/apis/memory_banks/memory_banks.py b/llama_stack/apis/memory_banks/memory_banks.py index b037dfa66..21569beff 100644 --- a/llama_stack/apis/memory_banks/memory_banks.py +++ b/llama_stack/apis/memory_banks/memory_banks.py @@ -15,7 +15,7 @@ from typing import ( Union, ) -from llama_models.schema_utils import json_schema_type, webmethod +from llama_models.schema_utils import json_schema_type, register_schema, webmethod from pydantic import BaseModel, Field @@ -113,15 +113,18 @@ class GraphMemoryBank(MemoryBankResourceMixin): memory_bank_type: Literal[MemoryBankType.graph.value] = MemoryBankType.graph.value -MemoryBank = Annotated[ - Union[ - VectorMemoryBank, - KeyValueMemoryBank, - KeywordMemoryBank, - GraphMemoryBank, +MemoryBank = register_schema( + Annotated[ + Union[ + VectorMemoryBank, + KeyValueMemoryBank, + KeywordMemoryBank, + GraphMemoryBank, + ], + Field(discriminator="memory_bank_type"), ], - Field(discriminator="memory_bank_type"), -] + name="MemoryBank", +) class MemoryBankInput(BaseModel): diff --git a/llama_stack/apis/version.py b/llama_stack/apis/version.py index f178712ba..53ad6a854 100644 --- a/llama_stack/apis/version.py +++ b/llama_stack/apis/version.py @@ -4,4 +4,4 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -LLAMA_STACK_API_VERSION = "alpha" +LLAMA_STACK_API_VERSION = "v1" diff --git a/llama_stack/distribution/stack.py b/llama_stack/distribution/stack.py index acbd42fa9..e3edf1e16 100644 --- a/llama_stack/distribution/stack.py +++ b/llama_stack/distribution/stack.py @@ -40,8 +40,6 @@ from llama_stack.providers.datatypes import Api log = logging.getLogger(__name__) -LLAMA_STACK_API_VERSION = "alpha" - class LlamaStack( MemoryBanks, From 32d3abe96423e47eca59eb6074307fd90dfec6c9 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Wed, 15 Jan 2025 09:01:33 -0800 Subject: [PATCH 013/107] [CICD] Github workflow for publishing Docker images (#764) # What does this PR do? - Add Github workflow for publishing docker images. - Manual Inputs - We can use a (1) TestPyPi version / (2) build via released PyPi version **Notes** - Keep this workflow manually triggered as we don't want to publish nightly docker images **Additional Changes** - Resolve issue with running llama stack build in non-terminal device ``` File "/home/runner/.local/lib/python3.12/site-packages/llama_stack/distribution/utils/exec.py", line 25, in run_with_pty old_settings = termios.tcgetattr(sys.stdin) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ termios.error: (25, 'Inappropriate ioctl for device') ``` - Modified build_container.sh to work in non-terminal environment ## Test Plan - Triggered workflow: https://github.com/meta-llama/llama-stack/actions/runs/12778759161/job/35622178782 image - Tested published docker image image - /tools API endpoints are served so that docker is correctly using the TestPyPi package image - Published tagged images: https://hub.docker.com/repositories/llamastack image ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- .github/workflows/publish-to-docker.yml | 99 +++++++++++++++++++++ llama_stack/cli/stack/build.py | 7 +- llama_stack/distribution/build.py | 15 +++- llama_stack/distribution/build_container.sh | 31 ++++--- llama_stack/distribution/utils/exec.py | 14 +-- 5 files changed, 145 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/publish-to-docker.yml diff --git a/.github/workflows/publish-to-docker.yml b/.github/workflows/publish-to-docker.yml new file mode 100644 index 000000000..cf1e8b916 --- /dev/null +++ b/.github/workflows/publish-to-docker.yml @@ -0,0 +1,99 @@ +name: Docker Build and Publish + +on: + workflow_dispatch: + inputs: + version: + description: 'TestPyPI or PyPI version to build (e.g., 0.0.63.dev20250114)' + required: true + type: string + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set version + id: version + run: | + if [ "${{ github.event_name }}" = "push" ]; then + echo "VERSION=0.0.63.dev20250114" >> $GITHUB_OUTPUT + else + echo "VERSION=${{ inputs.version }}" >> $GITHUB_OUTPUT + fi + + - name: Check package version availability + run: | + # Function to check if version exists in a repository + check_version() { + local repo=$1 + local status_code=$(curl -s -o /dev/null -w "%{http_code}" "https://$repo.org/project/llama-stack/${{ steps.version.outputs.version }}") + return $([ "$status_code" -eq 200 ]) + } + + # Check TestPyPI first, then PyPI + if check_version "test.pypi"; then + echo "Version ${{ steps.version.outputs.version }} found in TestPyPI" + echo "PYPI_SOURCE=testpypi" >> $GITHUB_ENV + elif check_version "pypi"; then + echo "Version ${{ steps.version.outputs.version }} found in PyPI" + echo "PYPI_SOURCE=pypi" >> $GITHUB_ENV + else + echo "Error: Version ${{ steps.version.outputs.version }} not found in either TestPyPI or PyPI" + exit 1 + fi + + - name: Install llama-stack + run: | + if [ "${{ github.event_name }}" = "push" ]; then + pip install -e . + else + if [ "$PYPI_SOURCE" = "testpypi" ]; then + pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple llama-stack==${{ steps.version.outputs.version }} + else + pip install llama-stack==${{ steps.version.outputs.version }} + fi + fi + + - name: Build docker image + run: | + TEMPLATES=("ollama" "bedrock" "remote-vllm" "fireworks" "together" "tgi" "meta-reference-gpu") + for template in "${TEMPLATES[@]}"; do + if [ "$PYPI_SOURCE" = "testpypi" ]; then + TEST_PYPI_VERSION=${{ steps.version.outputs.version }} llama stack build --template $template --image-type docker + else + PYPI_VERSION=${{ steps.version.outputs.version }} llama stack build --template $template --image-type docker + fi + done + + - name: List docker images + run: | + docker images + + - name: Push to dockerhub + run: | + TEMPLATES=("ollama" "bedrock" "remote-vllm" "fireworks" "together" "tgi" "meta-reference-gpu") + for template in "${TEMPLATES[@]}"; do + if [ "$PYPI_SOURCE" = "testpypi" ]; then + docker tag distribution-$template:test-${{ steps.version.outputs.version }} llamastack/distribution-$template:test-${{ steps.version.outputs.version }} + docker push llamastack/distribution-$template:test-${{ steps.version.outputs.version }} + else + docker tag distribution-$template:${{ steps.version.outputs.version }} llamastack/distribution-$template:${{ steps.version.outputs.version }} + docker push llamastack/distribution-$template:${{ steps.version.outputs.version }} + fi + done diff --git a/llama_stack/cli/stack/build.py b/llama_stack/cli/stack/build.py index 85e6cb962..38994bebf 100644 --- a/llama_stack/cli/stack/build.py +++ b/llama_stack/cli/stack/build.py @@ -107,7 +107,8 @@ class StackBuild(Subcommand): f"Please specify a image-type (docker | conda | venv) for {args.template}" ) self._run_stack_build_command_from_build_config( - build_config, template_name=args.template + build_config, + template_name=args.template, ) return @@ -261,7 +262,9 @@ class StackBuild(Subcommand): ) def _run_stack_build_command_from_build_config( - self, build_config: BuildConfig, template_name: Optional[str] = None + self, + build_config: BuildConfig, + template_name: Optional[str] = None, ) -> None: import json import os diff --git a/llama_stack/distribution/build.py b/llama_stack/distribution/build.py index 5a7dfba11..a8b2342af 100644 --- a/llama_stack/distribution/build.py +++ b/llama_stack/distribution/build.py @@ -6,6 +6,7 @@ import importlib.resources import logging +import sys from enum import Enum from pathlib import Path @@ -20,7 +21,7 @@ from llama_stack.distribution.distribution import get_provider_registry from llama_stack.distribution.utils.config_dirs import BUILDS_BASE_DIR -from llama_stack.distribution.utils.exec import run_with_pty +from llama_stack.distribution.utils.exec import run_command, run_with_pty from llama_stack.providers.datatypes import Api log = logging.getLogger(__name__) @@ -102,7 +103,10 @@ def print_pip_install_help(providers: Dict[str, List[Provider]]): print() -def build_image(build_config: BuildConfig, build_file_path: Path): +def build_image( + build_config: BuildConfig, + build_file_path: Path, +): docker_image = build_config.distribution_spec.docker_image or "python:3.10-slim" normal_deps, special_deps = get_provider_dependencies( @@ -144,7 +148,12 @@ def build_image(build_config: BuildConfig, build_file_path: Path): if special_deps: args.append("#".join(special_deps)) - return_code = run_with_pty(args) + is_terminal = sys.stdin.isatty() + if is_terminal: + return_code = run_with_pty(args) + else: + return_code = run_command(args) + if return_code != 0: log.error( f"Failed to build target {build_config.name} with return code {return_code}", diff --git a/llama_stack/distribution/build_container.sh b/llama_stack/distribution/build_container.sh index 06cb19c32..17902de0a 100755 --- a/llama_stack/distribution/build_container.sh +++ b/llama_stack/distribution/build_container.sh @@ -54,7 +54,7 @@ add_to_docker() { # Update and install UBI9 components if UBI9 base image is used if [[ $docker_base == *"registry.access.redhat.com/ubi9"* ]]; then - add_to_docker < Date: Wed, 15 Jan 2025 11:20:23 -0800 Subject: [PATCH 014/107] [bugfix] fix llama guard parsing ContentDelta (#772) # What does this PR do? Fix this error image ## Test Plan ``` LLAMA_STACK_BASE_URL="http://localhost:5000" pytest -v tests/client-sdk/inference/test_inference.py ``` ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- .../providers/inline/safety/llama_guard/llama_guard.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/llama_stack/providers/inline/safety/llama_guard/llama_guard.py b/llama_stack/providers/inline/safety/llama_guard/llama_guard.py index 00213ac83..bc4d9640c 100644 --- a/llama_stack/providers/inline/safety/llama_guard/llama_guard.py +++ b/llama_stack/providers/inline/safety/llama_guard/llama_guard.py @@ -263,9 +263,11 @@ class LlamaGuardShield: stream=True, ): event = chunk.event - if event.event_type == ChatCompletionResponseEventType.progress: - assert isinstance(event.delta, str) - content += event.delta + if ( + event.event_type == ChatCompletionResponseEventType.progress + and event.delta.type == "text" + ): + content += event.delta.text content = content.strip() return self.get_shield_response(content) From 6deef1ece09d4b4d60232a6c40737ad034b02c73 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Wed, 15 Jan 2025 12:55:19 -0800 Subject: [PATCH 015/107] rebase eval test w/ tool_runtime fixtures (#773) # What does this PR do? - fix eval tests to include tool_runtime fixtures - rebase eval for extracting memory retrieval context ## Test Plan ``` pytest -v -s -m meta_reference_eval_together_inference_huggingface_datasetio llama_stack/providers/tests/eval/test_eval.py pytest -v -s -m braintrust_scoring_together_inference llama_stack/providers/tests/scoring/test_scoring.py ``` - With notebook: https://gist.github.com/yanxi0830/1260a6cb7ec42498a195b88422462a34 ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- .../providers/inline/eval/meta_reference/eval.py | 11 +++++++++-- llama_stack/providers/tests/eval/conftest.py | 5 +++++ llama_stack/providers/tests/eval/fixtures.py | 11 ++++++++++- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/llama_stack/providers/inline/eval/meta_reference/eval.py b/llama_stack/providers/inline/eval/meta_reference/eval.py index 408043db8..63c1e8d98 100644 --- a/llama_stack/providers/inline/eval/meta_reference/eval.py +++ b/llama_stack/providers/inline/eval/meta_reference/eval.py @@ -16,6 +16,9 @@ from llama_stack.apis.scoring import Scoring from llama_stack.distribution.datatypes import Api from llama_stack.providers.datatypes import EvalTasksProtocolPrivate +from llama_stack.providers.inline.agents.meta_reference.agent_instance import ( + MEMORY_QUERY_TOOL, +) from llama_stack.providers.utils.common.data_schema_validator import ( ColumnName, get_valid_schemas, @@ -146,8 +149,12 @@ class MetaReferenceEvalImpl( # check if there's a memory retrieval step and extract the context memory_rag_context = None for step in final_event.turn.steps: - if step.step_type == StepType.memory_retrieval.value: - memory_rag_context = " ".join(x.text for x in step.inserted_context) + if step.step_type == StepType.tool_execution.value: + for tool_response in step.tool_responses: + if tool_response.tool_name == MEMORY_QUERY_TOOL: + memory_rag_context = " ".join( + x.text for x in tool_response.content + ) agent_generation = {} agent_generation[ColumnName.generated_answer.value] = ( diff --git a/llama_stack/providers/tests/eval/conftest.py b/llama_stack/providers/tests/eval/conftest.py index 1bb49d41f..3d6ef01b2 100644 --- a/llama_stack/providers/tests/eval/conftest.py +++ b/llama_stack/providers/tests/eval/conftest.py @@ -15,6 +15,7 @@ from ..inference.fixtures import INFERENCE_FIXTURES from ..memory.fixtures import MEMORY_FIXTURES from ..safety.fixtures import SAFETY_FIXTURES from ..scoring.fixtures import SCORING_FIXTURES +from ..tools.fixtures import TOOL_RUNTIME_FIXTURES from .fixtures import EVAL_FIXTURES DEFAULT_PROVIDER_COMBINATIONS = [ @@ -27,6 +28,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ "agents": "meta_reference", "safety": "llama_guard", "memory": "faiss", + "tool_runtime": "memory_and_search", }, id="meta_reference_eval_fireworks_inference", marks=pytest.mark.meta_reference_eval_fireworks_inference, @@ -40,6 +42,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ "agents": "meta_reference", "safety": "llama_guard", "memory": "faiss", + "tool_runtime": "memory_and_search", }, id="meta_reference_eval_together_inference", marks=pytest.mark.meta_reference_eval_together_inference, @@ -53,6 +56,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ "agents": "meta_reference", "safety": "llama_guard", "memory": "faiss", + "tool_runtime": "memory_and_search", }, id="meta_reference_eval_together_inference_huggingface_datasetio", marks=pytest.mark.meta_reference_eval_together_inference_huggingface_datasetio, @@ -98,6 +102,7 @@ def pytest_generate_tests(metafunc): "agents": AGENTS_FIXTURES, "safety": SAFETY_FIXTURES, "memory": MEMORY_FIXTURES, + "tool_runtime": TOOL_RUNTIME_FIXTURES, } combinations = ( get_provider_fixture_overrides(metafunc.config, available_fixtures) diff --git a/llama_stack/providers/tests/eval/fixtures.py b/llama_stack/providers/tests/eval/fixtures.py index eba7c48a6..37bb0527a 100644 --- a/llama_stack/providers/tests/eval/fixtures.py +++ b/llama_stack/providers/tests/eval/fixtures.py @@ -35,7 +35,13 @@ EVAL_FIXTURES = ["meta_reference", "remote"] @pytest_asyncio.fixture(scope="session") -async def eval_stack(request, inference_model, judge_model): +async def eval_stack( + request, + inference_model, + judge_model, + tool_group_input_memory, + tool_group_input_tavily_search, +): fixture_dict = request.param providers = {} @@ -48,6 +54,7 @@ async def eval_stack(request, inference_model, judge_model): "agents", "safety", "memory", + "tool_runtime", ]: fixture = request.getfixturevalue(f"{key}_{fixture_dict[key]}") providers[key] = fixture.providers @@ -63,6 +70,7 @@ async def eval_stack(request, inference_model, judge_model): Api.agents, Api.safety, Api.memory, + Api.tool_runtime, ], providers, provider_data, @@ -73,6 +81,7 @@ async def eval_stack(request, inference_model, judge_model): judge_model, ] ], + tool_groups=[tool_group_input_memory, tool_group_input_tavily_search], ) return test_stack.impls From 7fb2c1c48dee7dfb723fc12af176cc1e898957b9 Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Wed, 15 Jan 2025 13:20:09 -0800 Subject: [PATCH 016/107] More idiomatic REST API (#765) # What does this PR do? This PR changes our API to follow more idiomatic REST API approaches of having paths being resources and methods indicating the action being performed. Changes made to generator: 1) removed the prefix check of "get" as its not required and is actually needed for other method types too 2) removed _ check on path since variables can have "_" ## Test Plan LLAMA_STACK_BASE_URL=http://localhost:5000 pytest -v tests/client-sdk/agents/test_agents.py --- .../openapi_generator/pyopenapi/operations.py | 58 +- docs/resources/llama-stack-spec.html | 2173 +++++++++-------- docs/resources/llama-stack-spec.yaml | 1436 +++++------ llama_stack/apis/agents/agents.py | 44 +- .../apis/batch_inference/batch_inference.py | 4 +- llama_stack/apis/datasetio/datasetio.py | 4 +- llama_stack/apis/datasets/datasets.py | 16 +- llama_stack/apis/eval/eval.py | 16 +- llama_stack/apis/eval_tasks/eval_tasks.py | 18 +- llama_stack/apis/inference/inference.py | 6 +- llama_stack/apis/inspect/inspect.py | 6 +- llama_stack/apis/memory/memory.py | 4 +- llama_stack/apis/memory_banks/memory_banks.py | 20 +- llama_stack/apis/models/models.py | 24 +- .../apis/post_training/post_training.py | 9 +- llama_stack/apis/safety/safety.py | 3 +- llama_stack/apis/scoring/scoring.py | 5 +- .../scoring_functions/scoring_functions.py | 17 +- llama_stack/apis/shields/shields.py | 12 +- llama_stack/apis/telemetry/telemetry.py | 4 +- llama_stack/apis/tools/tools.py | 36 +- .../distribution/routers/routing_tables.py | 77 +- llama_stack/distribution/server/server.py | 40 +- llama_stack/distribution/stack.py | 6 +- .../agents/meta_reference/agent_instance.py | 8 +- .../inline/agents/meta_reference/agents.py | 2 +- .../post_training/torchtune/post_training.py | 7 +- .../telemetry/meta_reference/telemetry.py | 2 +- tests/client-sdk/agents/test_agents.py | 4 +- 29 files changed, 2144 insertions(+), 1917 deletions(-) diff --git a/docs/openapi_generator/pyopenapi/operations.py b/docs/openapi_generator/pyopenapi/operations.py index cc3a06b7b..4cea9d970 100644 --- a/docs/openapi_generator/pyopenapi/operations.py +++ b/docs/openapi_generator/pyopenapi/operations.py @@ -8,7 +8,6 @@ import collections.abc import enum import inspect import typing -import uuid from dataclasses import dataclass from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union @@ -16,12 +15,7 @@ from llama_stack.apis.version import LLAMA_STACK_API_VERSION from termcolor import colored -from ..strong_typing.inspection import ( - get_signature, - is_type_enum, - is_type_optional, - unwrap_optional_type, -) +from ..strong_typing.inspection import get_signature def split_prefix( @@ -113,9 +107,6 @@ class EndpointOperation: def get_route(self) -> str: if self.route is not None: - assert ( - "_" not in self.route - ), f"route should not contain underscores: {self.route}" return "/".join(["", LLAMA_STACK_API_VERSION, self.route.lstrip("/")]) route_parts = ["", LLAMA_STACK_API_VERSION, self.name] @@ -265,42 +256,16 @@ def get_endpoint_operations( f"parameter '{param_name}' in function '{func_name}' has no type annotation" ) - if is_type_optional(param_type): - inner_type: type = unwrap_optional_type(param_type) - else: - inner_type = param_type - - if prefix == "get" and ( - inner_type is bool - or inner_type is int - or inner_type is float - or inner_type is str - or inner_type is uuid.UUID - or is_type_enum(inner_type) - ): - if parameter.kind == inspect.Parameter.POSITIONAL_ONLY: - if route_params is not None and param_name not in route_params: - raise ValidationError( - f"positional parameter '{param_name}' absent from user-defined route '{route}' for function '{func_name}'" - ) - - # simple type maps to route path element, e.g. /study/{uuid}/{version} + if prefix in ["get", "delete"]: + if route_params is not None and param_name in route_params: path_params.append((param_name, param_type)) else: - if route_params is not None and param_name in route_params: - raise ValidationError( - f"query parameter '{param_name}' found in user-defined route '{route}' for function '{func_name}'" - ) - - # simple type maps to key=value pair in query string query_params.append((param_name, param_type)) else: if route_params is not None and param_name in route_params: - raise ValidationError( - f"user-defined route '{route}' for function '{func_name}' has parameter '{param_name}' of composite type: {param_type}" - ) - - request_params.append((param_name, param_type)) + path_params.append((param_name, param_type)) + else: + request_params.append((param_name, param_type)) # check if function has explicit return type if signature.return_annotation is inspect.Signature.empty: @@ -335,19 +300,18 @@ def get_endpoint_operations( response_type = process_type(return_type) - # set HTTP request method based on type of request and presence of payload - if not request_params: if prefix in ["delete", "remove"]: http_method = HTTPMethod.DELETE - else: + elif prefix == "post": + http_method = HTTPMethod.POST + elif prefix == "get": http_method = HTTPMethod.GET - else: - if prefix == "set": + elif prefix == "set": http_method = HTTPMethod.PUT elif prefix == "update": http_method = HTTPMethod.PATCH else: - http_method = HTTPMethod.POST + raise ValidationError(f"unknown prefix {prefix}") result.append( EndpointOperation( diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index 3f74a79cf..2db33c87a 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -29,7 +29,76 @@ } ], "paths": { - "/v1/datasetio/append-rows": { + "/v1/datasetio/rows": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PaginatedRowsResult" + } + } + } + } + }, + "tags": [ + "DatasetIO" + ], + "parameters": [ + { + "name": "dataset_id", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "rows_in_page", + "in": "query", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "page_token", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "filter_condition", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + }, "post": { "responses": { "200": { @@ -323,7 +392,7 @@ } } }, - "/v1/agents/create": { + "/v1/agents": { "post": { "responses": { "200": { @@ -372,7 +441,7 @@ } } }, - "/v1/agents/session/create": { + "/v1/agents/{agent_id}/session": { "post": { "responses": { "200": { @@ -390,6 +459,14 @@ "Agents" ], "parameters": [ + { + "name": "agent_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -421,7 +498,7 @@ } } }, - "/v1/agents/turn/create": { + "/v1/agents/{agent_id}/session/{session_id}/turn": { "post": { "responses": { "200": { @@ -446,6 +523,22 @@ "Agents" ], "parameters": [ + { + "name": "agent_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -477,8 +570,8 @@ } } }, - "/v1/agents/delete": { - "post": { + "/v1/agents/{agent_id}": { + "delete": { "responses": { "200": { "description": "OK" @@ -488,6 +581,14 @@ "Agents" ], "parameters": [ + { + "name": "agent_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -506,30 +607,54 @@ "type": "string" } } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeleteAgentsRequest" - } - } - }, - "required": true - } + ] } }, - "/v1/agents/session/delete": { - "post": { + "/v1/agents/{agent_id}/session/{session_id}": { + "get": { "responses": { "200": { - "description": "OK" + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Session" + } + } + } } }, "tags": [ "Agents" ], "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "agent_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "turn_ids", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -548,17 +673,53 @@ "type": "string" } } + ] + }, + "delete": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "Agents" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DeleteAgentsSessionRequest" - } + "parameters": [ + { + "name": "session_id", + "in": "path", + "required": true, + "schema": { + "type": "string" } }, - "required": true - } + { + "name": "agent_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, "/v1/inference/embeddings": { @@ -659,72 +820,7 @@ } } }, - "/v1/agents/session/get": { - "post": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Session" - } - } - } - } - }, - "tags": [ - "Agents" - ], - "parameters": [ - { - "name": "agent_id", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "session_id", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetAgentsSessionRequest" - } - } - }, - "required": true - } - } - }, - "/v1/agents/step/get": { + "/v1/agents/{agent_id}/session/{session_id}/turn/{turn_id}/step/{step_id}": { "get": { "responses": { "200": { @@ -744,7 +840,7 @@ "parameters": [ { "name": "agent_id", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -752,7 +848,7 @@ }, { "name": "session_id", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -760,7 +856,7 @@ }, { "name": "turn_id", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -768,7 +864,7 @@ }, { "name": "step_id", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -795,7 +891,7 @@ ] } }, - "/v1/agents/turn/get": { + "/v1/agents/{agent_id}/session/{session_id}/turn/{turn_id}": { "get": { "responses": { "200": { @@ -815,7 +911,7 @@ "parameters": [ { "name": "agent_id", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -823,7 +919,7 @@ }, { "name": "session_id", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -831,7 +927,7 @@ }, { "name": "turn_id", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -858,7 +954,7 @@ ] } }, - "/v1/datasets/get": { + "/v1/datasets/{dataset_id}": { "get": { "responses": { "200": { @@ -885,7 +981,45 @@ "parameters": [ { "name": "dataset_id", - "in": "query", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + }, + "delete": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "Datasets" + ], + "parameters": [ + { + "name": "dataset_id", + "in": "path", "required": true, "schema": { "type": "string" @@ -912,7 +1046,7 @@ ] } }, - "/v1/eval-tasks/get": { + "/v1/eval-tasks/{eval_task_id}": { "get": { "responses": { "200": { @@ -938,8 +1072,8 @@ ], "parameters": [ { - "name": "name", - "in": "query", + "name": "eval_task_id", + "in": "path", "required": true, "schema": { "type": "string" @@ -966,7 +1100,7 @@ ] } }, - "/v1/memory-banks/get": { + "/v1/memory-banks/{memory_bank_id}": { "get": { "responses": { "200": { @@ -993,7 +1127,45 @@ "parameters": [ { "name": "memory_bank_id", - "in": "query", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + }, + "delete": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "MemoryBanks" + ], + "parameters": [ + { + "name": "memory_bank_id", + "in": "path", "required": true, "schema": { "type": "string" @@ -1020,7 +1192,7 @@ ] } }, - "/v1/models/get": { + "/v1/models/{model_id}": { "get": { "responses": { "200": { @@ -1046,8 +1218,8 @@ ], "parameters": [ { - "name": "identifier", - "in": "query", + "name": "model_id", + "in": "path", "required": true, "schema": { "type": "string" @@ -1072,58 +1244,25 @@ } } ] - } - }, - "/v1/datasetio/get-rows-paginated": { - "get": { + }, + "delete": { "responses": { "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PaginatedRowsResult" - } - } - } + "description": "OK" } }, "tags": [ - "DatasetIO" + "Models" ], "parameters": [ { - "name": "dataset_id", - "in": "query", + "name": "model_id", + "in": "path", "required": true, "schema": { "type": "string" } }, - { - "name": "rows_in_page", - "in": "query", - "required": true, - "schema": { - "type": "integer" - } - }, - { - "name": "page_token", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "filter_condition", - "in": "query", - "required": false, - "schema": { - "type": "string" - } - }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -1145,7 +1284,7 @@ ] } }, - "/v1/scoring-functions/get": { + "/v1/scoring-functions/{scoring_fn_id}": { "get": { "responses": { "200": { @@ -1172,7 +1311,7 @@ "parameters": [ { "name": "scoring_fn_id", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -1199,7 +1338,7 @@ ] } }, - "/v1/shields/get": { + "/v1/shields/{identifier}": { "get": { "responses": { "200": { @@ -1226,7 +1365,7 @@ "parameters": [ { "name": "identifier", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -1253,75 +1392,7 @@ ] } }, - "/v1/telemetry/get-span-tree": { - "post": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/SpanWithStatus" - } - } - } - } - } - }, - "tags": [ - "Telemetry" - ], - "parameters": [ - { - "name": "span_id", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "max_depth", - "in": "query", - "required": false, - "schema": { - "type": "integer" - } - }, - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetSpanTreeRequest" - } - } - }, - "required": true - } - } - }, - "/v1/tools/get": { + "/v1/tools/{tool_name}": { "get": { "responses": { "200": { @@ -1341,7 +1412,7 @@ "parameters": [ { "name": "tool_name", - "in": "query", + "in": "path", "required": true, "schema": { "type": "string" @@ -1368,7 +1439,7 @@ ] } }, - "/v1/toolgroups/get": { + "/v1/toolgroups/{toolgroup_id}": { "get": { "responses": { "200": { @@ -1388,7 +1459,46 @@ "parameters": [ { "name": "toolgroup_id", - "in": "query", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + }, + "delete": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "ToolGroups" + ], + "summary": "Unregister a tool group", + "parameters": [ + { + "name": "toolgroup_id", + "in": "path", "required": true, "schema": { "type": "string" @@ -1529,9 +1639,9 @@ "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { "schema": { - "$ref": "#/components/schemas/PostTrainingJob" + "$ref": "#/components/schemas/ListPostTrainingJobsResponse" } } } @@ -1693,7 +1803,7 @@ } } }, - "/v1/eval/job/cancel": { + "/v1/eval/jobs/cancel": { "post": { "responses": { "200": { @@ -1735,7 +1845,7 @@ } } }, - "/v1/eval/job/result": { + "/v1/eval/jobs/{job_id}/result": { "get": { "responses": { "200": { @@ -1754,15 +1864,15 @@ ], "parameters": [ { - "name": "task_id", - "in": "query", + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string" } }, { - "name": "job_id", + "name": "task_id", "in": "query", "required": true, "schema": { @@ -1790,7 +1900,7 @@ ] } }, - "/v1/eval/job/status": { + "/v1/eval/jobs/{job_id}": { "get": { "responses": { "200": { @@ -1816,15 +1926,15 @@ ], "parameters": [ { - "name": "task_id", - "in": "query", + "name": "job_id", + "in": "path", "required": true, "schema": { "type": "string" } }, { - "name": "job_id", + "name": "task_id", "in": "query", "required": true, "schema": { @@ -1852,15 +1962,15 @@ ] } }, - "/v1/datasets/list": { + "/v1/datasets": { "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { "schema": { - "$ref": "#/components/schemas/Dataset" + "$ref": "#/components/schemas/ListDatasetsResponse" } } } @@ -1889,17 +1999,57 @@ } } ] + }, + "post": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "Datasets" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterDatasetRequest" + } + } + }, + "required": true + } } }, - "/v1/eval-tasks/list": { + "/v1/eval-tasks": { "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { "schema": { - "$ref": "#/components/schemas/EvalTask" + "$ref": "#/components/schemas/ListEvalTasksResponse" } } } @@ -1928,17 +2078,57 @@ } } ] + }, + "post": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "EvalTasks" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterEvalTaskRequest" + } + } + }, + "required": true + } } }, - "/v1/memory-banks/list": { + "/v1/memory-banks": { "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { "schema": { - "$ref": "#/components/schemas/MemoryBank" + "$ref": "#/components/schemas/ListMemoryBanksResponse" } } } @@ -1967,15 +2157,112 @@ } } ] + }, + "post": { + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VectorMemoryBank" + }, + { + "$ref": "#/components/schemas/KeyValueMemoryBank" + }, + { + "$ref": "#/components/schemas/KeywordMemoryBank" + }, + { + "$ref": "#/components/schemas/GraphMemoryBank" + } + ] + } + } + } + } + }, + "tags": [ + "MemoryBanks" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterMemoryBankRequest" + } + } + }, + "required": true + } } }, - "/v1/models/list": { + "/v1/models": { "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListModelsResponse" + } + } + } + } + }, + "tags": [ + "Models" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + }, + "post": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/Model" } @@ -2005,7 +2292,17 @@ "type": "string" } } - ] + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterModelRequest" + } + } + }, + "required": true + } } }, "/v1/providers/list": { @@ -2016,10 +2313,7 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/ProviderInfo" - } + "$ref": "#/components/schemas/ListProvidersResponse" } } } @@ -2096,7 +2390,7 @@ } }, "/v1/tool-runtime/list-tools": { - "post": { + "get": { "responses": { "200": { "description": "OK", @@ -2121,6 +2415,14 @@ "type": "string" } }, + { + "name": "mcp_endpoint", + "in": "query", + "required": false, + "schema": { + "$ref": "#/components/schemas/URL" + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -2139,28 +2441,18 @@ "type": "string" } } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListRuntimeToolsRequest" - } - } - }, - "required": true - } + ] } }, - "/v1/scoring-functions/list": { + "/v1/scoring-functions": { "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { "schema": { - "$ref": "#/components/schemas/ScoringFn" + "$ref": "#/components/schemas/ListScoringFunctionsResponse" } } } @@ -2189,15 +2481,92 @@ } } ] + }, + "post": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "ScoringFunctions" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterScoringFunctionRequest" + } + } + }, + "required": true + } } }, - "/v1/shields/list": { + "/v1/shields": { "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListShieldsResponse" + } + } + } + } + }, + "tags": [ + "Shields" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + }, + "post": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/Shield" } @@ -2227,18 +2596,28 @@ "type": "string" } } - ] + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterShieldRequest" + } + } + }, + "required": true + } } }, - "/v1/toolgroups/list": { + "/v1/toolgroups": { "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { "schema": { - "$ref": "#/components/schemas/ToolGroup" + "$ref": "#/components/schemas/ListToolGroupsResponse" } } } @@ -2268,17 +2647,58 @@ } } ] + }, + "post": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "ToolGroups" + ], + "summary": "Register a tool group", + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterToolGroupRequest" + } + } + }, + "required": true + } } }, - "/v1/tools/list": { + "/v1/tools": { "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { "schema": { - "$ref": "#/components/schemas/Tool" + "$ref": "#/components/schemas/ListToolsResponse" } } } @@ -2290,7 +2710,7 @@ "summary": "List tools with optional tool group", "parameters": [ { - "name": "tool_group_id", + "name": "toolgroup_id", "in": "query", "required": false, "schema": { @@ -2458,6 +2878,58 @@ } } }, + "/v1/telemetry/query-span-tree": { + "post": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/SpanWithStatus" + } + } + } + } + } + }, + "tags": [ + "Telemetry" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuerySpanTreeRequest" + } + } + }, + "required": true + } + } + }, "/v1/telemetry/query-spans": { "post": { "responses": { @@ -2556,336 +3028,7 @@ } } }, - "/v1/datasets/register": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "Datasets" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterDatasetRequest" - } - } - }, - "required": true - } - } - }, - "/v1/eval-tasks/register": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "EvalTasks" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterEvalTaskRequest" - } - } - }, - "required": true - } - } - }, - "/v1/memory-banks/register": { - "post": { - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/VectorMemoryBank" - }, - { - "$ref": "#/components/schemas/KeyValueMemoryBank" - }, - { - "$ref": "#/components/schemas/KeywordMemoryBank" - }, - { - "$ref": "#/components/schemas/GraphMemoryBank" - } - ] - } - } - } - } - }, - "tags": [ - "MemoryBanks" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterMemoryBankRequest" - } - } - }, - "required": true - } - } - }, - "/v1/models/register": { - "post": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Model" - } - } - } - } - }, - "tags": [ - "Models" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterModelRequest" - } - } - }, - "required": true - } - } - }, - "/v1/scoring-functions/register": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "ScoringFunctions" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterScoringFunctionRequest" - } - } - }, - "required": true - } - } - }, - "/v1/shields/register": { - "post": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Shield" - } - } - } - } - }, - "tags": [ - "Shields" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterShieldRequest" - } - } - }, - "required": true - } - } - }, - "/v1/toolgroups/register": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "ToolGroups" - ], - "summary": "Register a tool group", - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterToolGroupRequest" - } - } - }, - "required": true - } - } - }, - "/v1/eval/run-eval": { + "/v1/eval/run": { "post": { "responses": { "200": { @@ -3221,175 +3364,6 @@ } } }, - "/v1/datasets/unregister": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "Datasets" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnregisterDatasetRequest" - } - } - }, - "required": true - } - } - }, - "/v1/memory-banks/unregister": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "MemoryBanks" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnregisterMemoryBankRequest" - } - } - }, - "required": true - } - } - }, - "/v1/models/unregister": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "Models" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnregisterModelRequest" - } - } - }, - "required": true - } - } - }, - "/v1/toolgroups/unregister": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "ToolGroups" - ], - "summary": "Unregister a tool group", - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UnregisterToolGroupRequest" - } - } - }, - "required": true - } - } - }, "/v1/version": { "get": { "responses": { @@ -3512,6 +3486,20 @@ "tool_calls" ] }, + "GreedySamplingStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "greedy", + "default": "greedy" + } + }, + "additionalProperties": false, + "required": [ + "type" + ] + }, "ImageContentItem": { "type": "object", "properties": { @@ -3579,20 +3567,17 @@ "type": "object", "properties": { "strategy": { - "$ref": "#/components/schemas/SamplingStrategy", - "default": "greedy" - }, - "temperature": { - "type": "number", - "default": 0.0 - }, - "top_p": { - "type": "number", - "default": 0.95 - }, - "top_k": { - "type": "integer", - "default": 0 + "oneOf": [ + { + "$ref": "#/components/schemas/GreedySamplingStrategy" + }, + { + "$ref": "#/components/schemas/TopPSamplingStrategy" + }, + { + "$ref": "#/components/schemas/TopKSamplingStrategy" + } + ] }, "max_tokens": { "type": "integer", @@ -3608,14 +3593,6 @@ "strategy" ] }, - "SamplingStrategy": { - "type": "string", - "enum": [ - "greedy", - "top_p", - "top_k" - ] - }, "StopReason": { "type": "string", "enum": [ @@ -3869,6 +3846,45 @@ "content" ] }, + "TopKSamplingStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "top_k", + "default": "top_k" + }, + "top_k": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "type", + "top_k" + ] + }, + "TopPSamplingStrategy": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "top_p", + "default": "top_p" + }, + "temperature": { + "type": "number" + }, + "top_p": { + "type": "number", + "default": 0.95 + } + }, + "additionalProperties": false, + "required": [ + "type" + ] + }, "URL": { "type": "object", "properties": { @@ -4631,16 +4647,12 @@ "CreateAgentSessionRequest": { "type": "object", "properties": { - "agent_id": { - "type": "string" - }, "session_name": { "type": "string" } }, "additionalProperties": false, "required": [ - "agent_id", "session_name" ] }, @@ -4659,12 +4671,6 @@ "CreateAgentTurnRequest": { "type": "object", "properties": { - "agent_id": { - "type": "string" - }, - "session_id": { - "type": "string" - }, "messages": { "type": "array", "items": { @@ -4725,8 +4731,6 @@ }, "additionalProperties": false, "required": [ - "agent_id", - "session_id", "messages" ] }, @@ -5266,34 +5270,6 @@ "error" ] }, - "DeleteAgentsRequest": { - "type": "object", - "properties": { - "agent_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "agent_id" - ] - }, - "DeleteAgentsSessionRequest": { - "type": "object", - "properties": { - "agent_id": { - "type": "string" - }, - "session_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "agent_id", - "session_id" - ] - }, "EmbeddingsRequest": { "type": "object", "properties": { @@ -5701,18 +5677,6 @@ "aggregated_results" ] }, - "GetAgentsSessionRequest": { - "type": "object", - "properties": { - "turn_ids": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - }, "GraphMemoryBank": { "type": "object", "properties": { @@ -6431,85 +6395,6 @@ ], "title": "A safety shield resource that can be used to check content" }, - "GetSpanTreeRequest": { - "type": "object", - "properties": { - "attributes_to_return": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "SpanStatus": { - "type": "string", - "enum": [ - "ok", - "error" - ] - }, - "SpanWithStatus": { - "type": "object", - "properties": { - "span_id": { - "type": "string" - }, - "trace_id": { - "type": "string" - }, - "parent_span_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "start_time": { - "type": "string", - "format": "date-time" - }, - "end_time": { - "type": "string", - "format": "date-time" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "null" - }, - { - "type": "boolean" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "array" - }, - { - "type": "object" - } - ] - } - }, - "status": { - "$ref": "#/components/schemas/SpanStatus" - } - }, - "additionalProperties": false, - "required": [ - "span_id", - "trace_id", - "name", - "start_time" - ] - }, "Tool": { "type": "object", "properties": { @@ -6735,16 +6620,28 @@ ], "title": "Status of a finetuning job." }, - "PostTrainingJob": { + "ListPostTrainingJobsResponse": { "type": "object", "properties": { - "job_uuid": { - "type": "string" + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "job_uuid": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "job_uuid" + ] + } } }, "additionalProperties": false, "required": [ - "job_uuid" + "data" ] }, "HealthInfo": { @@ -6901,17 +6798,77 @@ "JobCancelRequest": { "type": "object", "properties": { - "task_id": { + "job_id": { "type": "string" }, - "job_id": { + "task_id": { "type": "string" } }, "additionalProperties": false, "required": [ - "task_id", - "job_id" + "job_id", + "task_id" + ] + }, + "ListDatasetsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Dataset" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, + "ListEvalTasksResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/EvalTask" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, + "ListMemoryBanksResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MemoryBank" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, + "ListModelsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Model" + } + } + }, + "additionalProperties": false, + "required": [ + "data" ] }, "ProviderInfo": { @@ -6930,6 +6887,21 @@ "provider_type" ] }, + "ListProvidersResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ProviderInfo" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, "RouteInfo": { "type": "object", "properties": { @@ -6953,14 +6925,65 @@ "provider_types" ] }, - "ListRuntimeToolsRequest": { + "ListScoringFunctionsResponse": { "type": "object", "properties": { - "mcp_endpoint": { - "$ref": "#/components/schemas/URL" + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ScoringFn" + } } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "data" + ] + }, + "ListShieldsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Shield" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, + "ListToolGroupsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ToolGroup" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, + "ListToolsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Tool" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] }, "LogSeverity": { "type": "string", @@ -7083,6 +7106,13 @@ "name" ] }, + "SpanStatus": { + "type": "string", + "enum": [ + "ok", + "error" + ] + }, "StructuredLogEvent": { "type": "object", "properties": { @@ -7467,6 +7497,18 @@ "logger_config" ] }, + "PostTrainingJob": { + "type": "object", + "properties": { + "job_uuid": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "job_uuid" + ] + }, "QueryDocumentsRequest": { "type": "object", "properties": { @@ -7547,6 +7589,87 @@ "scores" ] }, + "QuerySpanTreeRequest": { + "type": "object", + "properties": { + "span_id": { + "type": "string" + }, + "attributes_to_return": { + "type": "array", + "items": { + "type": "string" + } + }, + "max_depth": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "span_id" + ] + }, + "SpanWithStatus": { + "type": "object", + "properties": { + "span_id": { + "type": "string" + }, + "trace_id": { + "type": "string" + }, + "parent_span_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "start_time": { + "type": "string", + "format": "date-time" + }, + "end_time": { + "type": "string", + "format": "date-time" + }, + "attributes": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "array" + }, + { + "type": "object" + } + ] + } + }, + "status": { + "$ref": "#/components/schemas/SpanStatus" + } + }, + "additionalProperties": false, + "required": [ + "span_id", + "trace_id", + "name", + "start_time" + ] + }, "QueryCondition": { "type": "object", "properties": { @@ -8606,54 +8729,6 @@ ], "title": "Response from the synthetic data generation. Batch of (prompt, response, score) tuples that pass the threshold." }, - "UnregisterDatasetRequest": { - "type": "object", - "properties": { - "dataset_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "dataset_id" - ] - }, - "UnregisterMemoryBankRequest": { - "type": "object", - "properties": { - "memory_bank_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "memory_bank_id" - ] - }, - "UnregisterModelRequest": { - "type": "object", - "properties": { - "model_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "model_id" - ] - }, - "UnregisterToolGroupRequest": { - "type": "object", - "properties": { - "tool_group_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "tool_group_id" - ] - }, "VersionInfo": { "type": "object", "properties": { @@ -8855,14 +8930,6 @@ { "name": "Datasets" }, - { - "name": "DeleteAgentsRequest", - "description": "" - }, - { - "name": "DeleteAgentsSessionRequest", - "description": "" - }, { "name": "EfficiencyConfig", "description": "" @@ -8893,14 +8960,6 @@ "name": "EvaluateRowsRequest", "description": "" }, - { - "name": "GetAgentsSessionRequest", - "description": "" - }, - { - "name": "GetSpanTreeRequest", - "description": "" - }, { "name": "GraphMemoryBank", "description": "" @@ -8909,6 +8968,10 @@ "name": "GraphMemoryBankParams", "description": "" }, + { + "name": "GreedySamplingStrategy", + "description": "" + }, { "name": "HealthInfo", "description": "" @@ -8980,8 +9043,44 @@ "description": "" }, { - "name": "ListRuntimeToolsRequest", - "description": "" + "name": "ListDatasetsResponse", + "description": "" + }, + { + "name": "ListEvalTasksResponse", + "description": "" + }, + { + "name": "ListMemoryBanksResponse", + "description": "" + }, + { + "name": "ListModelsResponse", + "description": "" + }, + { + "name": "ListPostTrainingJobsResponse", + "description": "" + }, + { + "name": "ListProvidersResponse", + "description": "" + }, + { + "name": "ListScoringFunctionsResponse", + "description": "" + }, + { + "name": "ListShieldsResponse", + "description": "" + }, + { + "name": "ListToolGroupsResponse", + "description": "" + }, + { + "name": "ListToolsResponse", + "description": "" }, { "name": "LogEventRequest", @@ -9095,6 +9194,10 @@ "name": "QueryDocumentsResponse", "description": "" }, + { + "name": "QuerySpanTreeRequest", + "description": "" + }, { "name": "QuerySpansRequest", "description": "" @@ -9166,10 +9269,6 @@ "name": "SamplingParams", "description": "" }, - { - "name": "SamplingStrategy", - "description": "" - }, { "name": "SaveSpansToDatasetRequest", "description": "" @@ -9351,6 +9450,14 @@ { "name": "ToolRuntime" }, + { + "name": "TopKSamplingStrategy", + "description": "" + }, + { + "name": "TopPSamplingStrategy", + "description": "" + }, { "name": "Trace", "description": "" @@ -9367,22 +9474,6 @@ "name": "URL", "description": "" }, - { - "name": "UnregisterDatasetRequest", - "description": "" - }, - { - "name": "UnregisterMemoryBankRequest", - "description": "" - }, - { - "name": "UnregisterModelRequest", - "description": "" - }, - { - "name": "UnregisterToolGroupRequest", - "description": "" - }, { "name": "UnstructuredLogEvent", "description": "" @@ -9479,18 +9570,15 @@ "DataConfig", "Dataset", "DatasetFormat", - "DeleteAgentsRequest", - "DeleteAgentsSessionRequest", "EfficiencyConfig", "EmbeddingsRequest", "EmbeddingsResponse", "EvalTask", "EvaluateResponse", "EvaluateRowsRequest", - "GetAgentsSessionRequest", - "GetSpanTreeRequest", "GraphMemoryBank", "GraphMemoryBankParams", + "GreedySamplingStrategy", "HealthInfo", "ImageContentItem", "ImageDelta", @@ -9507,7 +9595,16 @@ "KeywordMemoryBank", "KeywordMemoryBankParams", "LLMAsJudgeScoringFnParams", - "ListRuntimeToolsRequest", + "ListDatasetsResponse", + "ListEvalTasksResponse", + "ListMemoryBanksResponse", + "ListModelsResponse", + "ListPostTrainingJobsResponse", + "ListProvidersResponse", + "ListScoringFunctionsResponse", + "ListShieldsResponse", + "ListToolGroupsResponse", + "ListToolsResponse", "LogEventRequest", "LogSeverity", "LoraFinetuningConfig", @@ -9533,6 +9630,7 @@ "QueryConditionOp", "QueryDocumentsRequest", "QueryDocumentsResponse", + "QuerySpanTreeRequest", "QuerySpansRequest", "QueryTracesRequest", "RegexParserScoringFnParams", @@ -9550,7 +9648,6 @@ "RunShieldResponse", "SafetyViolation", "SamplingParams", - "SamplingStrategy", "SaveSpansToDatasetRequest", "ScoreBatchRequest", "ScoreBatchResponse", @@ -9591,14 +9688,12 @@ "ToolPromptFormat", "ToolResponse", "ToolResponseMessage", + "TopKSamplingStrategy", + "TopPSamplingStrategy", "Trace", "TrainingConfig", "Turn", "URL", - "UnregisterDatasetRequest", - "UnregisterMemoryBankRequest", - "UnregisterModelRequest", - "UnregisterToolGroupRequest", "UnstructuredLogEvent", "UserMessage", "VectorMemoryBank", diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index 2afb8e375..ab27e4f3d 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -584,19 +584,14 @@ components: CreateAgentSessionRequest: additionalProperties: false properties: - agent_id: - type: string session_name: type: string required: - - agent_id - session_name type: object CreateAgentTurnRequest: additionalProperties: false properties: - agent_id: - type: string documents: items: additionalProperties: false @@ -622,8 +617,6 @@ components: - $ref: '#/components/schemas/UserMessage' - $ref: '#/components/schemas/ToolResponseMessage' type: array - session_id: - type: string stream: type: boolean toolgroups: @@ -631,8 +624,6 @@ components: $ref: '#/components/schemas/AgentTool' type: array required: - - agent_id - - session_id - messages type: object DPOAlignmentConfig: @@ -720,25 +711,6 @@ components: - instruct - dialog type: string - DeleteAgentsRequest: - additionalProperties: false - properties: - agent_id: - type: string - required: - - agent_id - type: object - DeleteAgentsSessionRequest: - additionalProperties: false - properties: - agent_id: - type: string - session_id: - type: string - required: - - agent_id - - session_id - type: object EfficiencyConfig: additionalProperties: false properties: @@ -872,22 +844,6 @@ components: - scoring_functions - task_config type: object - GetAgentsSessionRequest: - additionalProperties: false - properties: - turn_ids: - items: - type: string - type: array - type: object - GetSpanTreeRequest: - additionalProperties: false - properties: - attributes_to_return: - items: - type: string - type: array - type: object GraphMemoryBank: additionalProperties: false properties: @@ -922,6 +878,16 @@ components: required: - memory_bank_type type: object + GreedySamplingStrategy: + additionalProperties: false + properties: + type: + const: greedy + default: greedy + type: string + required: + - type + type: object HealthInfo: additionalProperties: false properties: @@ -1045,8 +1011,8 @@ components: task_id: type: string required: - - task_id - job_id + - task_id type: object JobStatus: enum: @@ -1146,11 +1112,111 @@ components: - type - judge_model type: object - ListRuntimeToolsRequest: + ListDatasetsResponse: additionalProperties: false properties: - mcp_endpoint: - $ref: '#/components/schemas/URL' + data: + items: + $ref: '#/components/schemas/Dataset' + type: array + required: + - data + type: object + ListEvalTasksResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/EvalTask' + type: array + required: + - data + type: object + ListMemoryBanksResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/MemoryBank' + type: array + required: + - data + type: object + ListModelsResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/Model' + type: array + required: + - data + type: object + ListPostTrainingJobsResponse: + additionalProperties: false + properties: + data: + items: + additionalProperties: false + properties: + job_uuid: + type: string + required: + - job_uuid + type: object + type: array + required: + - data + type: object + ListProvidersResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/ProviderInfo' + type: array + required: + - data + type: object + ListScoringFunctionsResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/ScoringFn' + type: array + required: + - data + type: object + ListShieldsResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/Shield' + type: array + required: + - data + type: object + ListToolGroupsResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/ToolGroup' + type: array + required: + - data + type: object + ListToolsResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/Tool' + type: array + required: + - data type: object LogEventRequest: additionalProperties: false @@ -1715,6 +1781,20 @@ components: - chunks - scores type: object + QuerySpanTreeRequest: + additionalProperties: false + properties: + attributes_to_return: + items: + type: string + type: array + max_depth: + type: integer + span_id: + type: string + required: + - span_id + type: object QuerySpansRequest: additionalProperties: false properties: @@ -2069,26 +2149,13 @@ components: default: 1.0 type: number strategy: - $ref: '#/components/schemas/SamplingStrategy' - default: greedy - temperature: - default: 0.0 - type: number - top_k: - default: 0 - type: integer - top_p: - default: 0.95 - type: number + oneOf: + - $ref: '#/components/schemas/GreedySamplingStrategy' + - $ref: '#/components/schemas/TopPSamplingStrategy' + - $ref: '#/components/schemas/TopKSamplingStrategy' required: - strategy type: object - SamplingStrategy: - enum: - - greedy - - top_p - - top_k - type: string SaveSpansToDatasetRequest: additionalProperties: false properties: @@ -2945,6 +3012,34 @@ components: - tool_name - content type: object + TopKSamplingStrategy: + additionalProperties: false + properties: + top_k: + type: integer + type: + const: top_k + default: top_k + type: string + required: + - type + - top_k + type: object + TopPSamplingStrategy: + additionalProperties: false + properties: + temperature: + type: number + top_p: + default: 0.95 + type: number + type: + const: top_p + default: top_p + type: string + required: + - type + type: object Trace: additionalProperties: false properties: @@ -3057,38 +3152,6 @@ components: required: - uri type: object - UnregisterDatasetRequest: - additionalProperties: false - properties: - dataset_id: - type: string - required: - - dataset_id - type: object - UnregisterMemoryBankRequest: - additionalProperties: false - properties: - memory_bank_id: - type: string - required: - - memory_bank_id - type: object - UnregisterModelRequest: - additionalProperties: false - properties: - model_id: - type: string - required: - - model_id - type: object - UnregisterToolGroupRequest: - additionalProperties: false - properties: - tool_group_id: - type: string - required: - - tool_group_id - type: object UnstructuredLogEvent: additionalProperties: false properties: @@ -3216,7 +3279,7 @@ info: jsonSchemaDialect: https://json-schema.org/draft/2020-12/schema openapi: 3.1.0 paths: - /v1/agents/create: + /v1/agents: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3248,9 +3311,14 @@ paths: description: OK tags: - Agents - /v1/agents/delete: - post: + /v1/agents/{agent_id}: + delete: parameters: + - in: path + name: agent_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -3265,20 +3333,19 @@ paths: required: false schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteAgentsRequest' - required: true responses: '200': description: OK tags: - Agents - /v1/agents/session/create: + /v1/agents/{agent_id}/session: post: parameters: + - in: path + name: agent_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -3308,9 +3375,19 @@ paths: description: OK tags: - Agents - /v1/agents/session/delete: - post: + /v1/agents/{agent_id}/session/{session_id}: + delete: parameters: + - in: path + name: session_id + required: true + schema: + type: string + - in: path + name: agent_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -3325,30 +3402,30 @@ paths: required: false schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/DeleteAgentsSessionRequest' - required: true responses: '200': description: OK tags: - Agents - /v1/agents/session/get: - post: + get: parameters: - - in: query + - in: path + name: session_id + required: true + schema: + type: string + - in: path name: agent_id required: true schema: type: string - in: query - name: session_id - required: true + name: turn_ids + required: false schema: - type: string + items: + type: string + type: array - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -3363,12 +3440,6 @@ paths: required: false schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GetAgentsSessionRequest' - required: true responses: '200': content: @@ -3378,55 +3449,19 @@ paths: description: OK tags: - Agents - /v1/agents/step/get: - get: + /v1/agents/{agent_id}/session/{session_id}/turn: + post: parameters: - - in: query + - in: path name: agent_id required: true schema: type: string - - in: query + - in: path name: session_id required: true schema: type: string - - in: query - name: turn_id - required: true - schema: - type: string - - in: query - name: step_id - required: true - schema: - type: string - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/AgentStepResponse' - description: OK - tags: - - Agents - /v1/agents/turn/create: - post: - parameters: - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -3459,20 +3494,20 @@ paths: streamed agent turn completion response. tags: - Agents - /v1/agents/turn/get: + /v1/agents/{agent_id}/session/{session_id}/turn/{turn_id}: get: parameters: - - in: query + - in: path name: agent_id required: true schema: type: string - - in: query + - in: path name: session_id required: true schema: type: string - - in: query + - in: path name: turn_id required: true schema: @@ -3500,6 +3535,52 @@ paths: description: OK tags: - Agents + /v1/agents/{agent_id}/session/{session_id}/turn/{turn_id}/step/{step_id}: + get: + parameters: + - in: path + name: agent_id + required: true + schema: + type: string + - in: path + name: session_id + required: true + schema: + type: string + - in: path + name: turn_id + required: true + schema: + type: string + - in: path + name: step_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/AgentStepResponse' + description: OK + tags: + - Agents /v1/batch-inference/chat-completion: post: parameters: @@ -3564,35 +3645,7 @@ paths: description: OK tags: - BatchInference (Coming Soon) - /v1/datasetio/append-rows: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/AppendRowsRequest' - required: true - responses: - '200': - description: OK - tags: - - DatasetIO - /v1/datasetio/get-rows-paginated: + /v1/datasetio/rows: get: parameters: - in: query @@ -3638,10 +3691,116 @@ paths: description: OK tags: - DatasetIO - /v1/datasets/get: + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AppendRowsRequest' + required: true + responses: + '200': + description: OK + tags: + - DatasetIO + /v1/datasets: get: parameters: - - in: query + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ListDatasetsResponse' + description: OK + tags: + - Datasets + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterDatasetRequest' + required: true + responses: + '200': + description: OK + tags: + - Datasets + /v1/datasets/{dataset_id}: + delete: + parameters: + - in: path + name: dataset_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + description: OK + tags: + - Datasets + get: + parameters: + - in: path name: dataset_id required: true schema: @@ -3671,7 +3830,7 @@ paths: description: OK tags: - Datasets - /v1/datasets/list: + /v1/eval-tasks: get: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3691,13 +3850,12 @@ paths: responses: '200': content: - application/jsonl: + application/json: schema: - $ref: '#/components/schemas/Dataset' + $ref: '#/components/schemas/ListEvalTasksResponse' description: OK tags: - - Datasets - /v1/datasets/register: + - EvalTasks post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3718,46 +3876,18 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterDatasetRequest' + $ref: '#/components/schemas/RegisterEvalTaskRequest' required: true responses: '200': description: OK tags: - - Datasets - /v1/datasets/unregister: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UnregisterDatasetRequest' - required: true - responses: - '200': - description: OK - tags: - - Datasets - /v1/eval-tasks/get: + - EvalTasks + /v1/eval-tasks/{eval_task_id}: get: parameters: - - in: query - name: name + - in: path + name: eval_task_id required: true schema: type: string @@ -3786,60 +3916,6 @@ paths: description: OK tags: - EvalTasks - /v1/eval-tasks/list: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/EvalTask' - description: OK - tags: - - EvalTasks - /v1/eval-tasks/register: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RegisterEvalTaskRequest' - required: true - responses: - '200': - description: OK - tags: - - EvalTasks /v1/eval/evaluate-rows: post: parameters: @@ -3872,7 +3948,7 @@ paths: description: OK tags: - Eval - /v1/eval/job/cancel: + /v1/eval/jobs/cancel: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -3900,55 +3976,19 @@ paths: description: OK tags: - Eval - /v1/eval/job/result: + /v1/eval/jobs/{job_id}: get: parameters: - - in: query - name: task_id - required: true - schema: - type: string - - in: query + - in: path name: job_id required: true schema: type: string - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/EvaluateResponse' - description: OK - tags: - - Eval - /v1/eval/job/status: - get: - parameters: - in: query name: task_id required: true schema: type: string - - in: query - name: job_id - required: true - schema: - type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -3974,7 +4014,43 @@ paths: description: OK tags: - Eval - /v1/eval/run-eval: + /v1/eval/jobs/{job_id}/result: + get: + parameters: + - in: path + name: job_id + required: true + schema: + type: string + - in: query + name: task_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/EvaluateResponse' + description: OK + tags: + - Eval + /v1/eval/run: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4132,14 +4208,9 @@ paths: description: OK tags: - Inference - /v1/memory-banks/get: + /v1/memory-banks: get: parameters: - - in: query - name: memory_bank_id - required: true - schema: - type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -4159,39 +4230,10 @@ paths: content: application/json: schema: - oneOf: - - $ref: '#/components/schemas/MemoryBank' - - type: 'null' + $ref: '#/components/schemas/ListMemoryBanksResponse' description: OK tags: - MemoryBanks - /v1/memory-banks/list: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/MemoryBank' - description: OK - tags: - - MemoryBanks - /v1/memory-banks/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4227,9 +4269,14 @@ paths: description: '' tags: - MemoryBanks - /v1/memory-banks/unregister: - post: + /v1/memory-banks/{memory_bank_id}: + delete: parameters: + - in: path + name: memory_bank_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -4244,17 +4291,43 @@ paths: required: false schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UnregisterMemoryBankRequest' - required: true responses: '200': description: OK tags: - MemoryBanks + get: + parameters: + - in: path + name: memory_bank_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/MemoryBank' + - type: 'null' + description: OK + tags: + - MemoryBanks /v1/memory/insert: post: parameters: @@ -4315,14 +4388,9 @@ paths: description: OK tags: - Memory - /v1/models/get: + /v1/models: get: parameters: - - in: query - name: identifier - required: true - schema: - type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -4342,39 +4410,10 @@ paths: content: application/json: schema: - oneOf: - - $ref: '#/components/schemas/Model' - - type: 'null' + $ref: '#/components/schemas/ListModelsResponse' description: OK tags: - Models - /v1/models/list: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/Model' - description: OK - tags: - - Models - /v1/models/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4406,9 +4445,14 @@ paths: description: OK tags: - Models - /v1/models/unregister: - post: + /v1/models/{model_id}: + delete: parameters: + - in: path + name: model_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -4423,17 +4467,43 @@ paths: required: false schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UnregisterModelRequest' - required: true responses: '200': description: OK tags: - Models + get: + parameters: + - in: path + name: model_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/Model' + - type: 'null' + description: OK + tags: + - Models /v1/post-training/job/artifacts: get: parameters: @@ -4548,9 +4618,9 @@ paths: responses: '200': content: - application/jsonl: + application/json: schema: - $ref: '#/components/schemas/PostTrainingJob' + $ref: '#/components/schemas/ListPostTrainingJobsResponse' description: OK tags: - PostTraining (Coming Soon) @@ -4640,9 +4710,7 @@ paths: content: application/json: schema: - additionalProperties: - $ref: '#/components/schemas/ProviderInfo' - type: object + $ref: '#/components/schemas/ListProvidersResponse' description: OK tags: - Inspect @@ -4708,10 +4776,63 @@ paths: description: OK tags: - Safety - /v1/scoring-functions/get: + /v1/scoring-functions: get: parameters: - - in: query + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ListScoringFunctionsResponse' + description: OK + tags: + - ScoringFunctions + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterScoringFunctionRequest' + required: true + responses: + '200': + description: OK + tags: + - ScoringFunctions + /v1/scoring-functions/{scoring_fn_id}: + get: + parameters: + - in: path name: scoring_fn_id required: true schema: @@ -4741,60 +4862,6 @@ paths: description: OK tags: - ScoringFunctions - /v1/scoring-functions/list: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/ScoringFn' - description: OK - tags: - - ScoringFunctions - /v1/scoring-functions/register: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RegisterScoringFunctionRequest' - required: true - responses: - '200': - description: OK - tags: - - ScoringFunctions /v1/scoring/score: post: parameters: @@ -4859,14 +4926,9 @@ paths: description: OK tags: - Scoring - /v1/shields/get: + /v1/shields: get: parameters: - - in: query - name: identifier - required: true - schema: - type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -4886,39 +4948,10 @@ paths: content: application/json: schema: - oneOf: - - $ref: '#/components/schemas/Shield' - - type: 'null' + $ref: '#/components/schemas/ListShieldsResponse' description: OK tags: - Shields - /v1/shields/list: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/Shield' - description: OK - tags: - - Shields - /v1/shields/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -4950,6 +4983,39 @@ paths: description: OK tags: - Shields + /v1/shields/{identifier}: + get: + parameters: + - in: path + name: identifier + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/Shield' + - type: 'null' + description: OK + tags: + - Shields /v1/synthetic-data-generation/generate: post: parameters: @@ -4982,50 +5048,6 @@ paths: description: OK tags: - SyntheticDataGeneration (Coming Soon) - /v1/telemetry/get-span-tree: - post: - parameters: - - in: query - name: span_id - required: true - schema: - type: string - - in: query - name: max_depth - required: false - schema: - type: integer - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/GetSpanTreeRequest' - required: true - responses: - '200': - content: - application/json: - schema: - additionalProperties: - $ref: '#/components/schemas/SpanWithStatus' - type: object - description: OK - tags: - - Telemetry /v1/telemetry/log-event: post: parameters: @@ -5054,6 +5076,40 @@ paths: description: OK tags: - Telemetry + /v1/telemetry/query-span-tree: + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QuerySpanTreeRequest' + required: true + responses: + '200': + content: + application/json: + schema: + additionalProperties: + $ref: '#/components/schemas/SpanWithStatus' + type: object + description: OK + tags: + - Telemetry /v1/telemetry/query-spans: post: parameters: @@ -5180,13 +5236,18 @@ paths: tags: - ToolRuntime /v1/tool-runtime/list-tools: - post: + get: parameters: - in: query name: tool_group_id required: false schema: type: string + - in: query + name: mcp_endpoint + required: false + schema: + $ref: '#/components/schemas/URL' - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -5201,12 +5262,6 @@ paths: required: false schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/ListRuntimeToolsRequest' - required: true responses: '200': content: @@ -5216,14 +5271,9 @@ paths: description: OK tags: - ToolRuntime - /v1/toolgroups/get: + /v1/toolgroups: get: parameters: - - in: query - name: toolgroup_id - required: true - schema: - type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -5243,38 +5293,11 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/ToolGroup' - description: OK - tags: - - ToolGroups - /v1/toolgroups/list: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/ToolGroup' + $ref: '#/components/schemas/ListToolGroupsResponse' description: OK summary: List tool groups with optional provider tags: - ToolGroups - /v1/toolgroups/register: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5303,9 +5326,14 @@ paths: summary: Register a tool group tags: - ToolGroups - /v1/toolgroups/unregister: - post: + /v1/toolgroups/{toolgroup_id}: + delete: parameters: + - in: path + name: toolgroup_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -5320,22 +5348,78 @@ paths: required: false schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/UnregisterToolGroupRequest' - required: true responses: '200': description: OK summary: Unregister a tool group tags: - ToolGroups - /v1/tools/get: + get: + parameters: + - in: path + name: toolgroup_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ToolGroup' + description: OK + tags: + - ToolGroups + /v1/tools: get: parameters: - in: query + name: toolgroup_id + required: false + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ListToolsResponse' + description: OK + summary: List tools with optional tool group + tags: + - ToolGroups + /v1/tools/{tool_name}: + get: + parameters: + - in: path name: tool_name required: true schema: @@ -5363,38 +5447,6 @@ paths: description: OK tags: - ToolGroups - /v1/tools/list: - get: - parameters: - - in: query - name: tool_group_id - required: false - schema: - type: string - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/Tool' - description: OK - summary: List tools with optional tool group - tags: - - ToolGroups /v1/version: get: parameters: @@ -5568,12 +5620,6 @@ tags: name: DatasetFormat - name: DatasetIO - name: Datasets -- description: - name: DeleteAgentsRequest -- description: - name: DeleteAgentsSessionRequest - description: name: EfficiencyConfig @@ -5593,18 +5639,15 @@ tags: - description: name: EvaluateRowsRequest -- description: - name: GetAgentsSessionRequest -- description: - name: GetSpanTreeRequest - description: name: GraphMemoryBank - description: name: GraphMemoryBankParams +- description: + name: GreedySamplingStrategy - description: name: HealthInfo - description: name: LLMAsJudgeScoringFnParams -- description: - name: ListRuntimeToolsRequest + name: ListDatasetsResponse +- description: + name: ListEvalTasksResponse +- description: + name: ListMemoryBanksResponse +- description: + name: ListModelsResponse +- description: + name: ListPostTrainingJobsResponse +- description: + name: ListProvidersResponse +- description: + name: ListScoringFunctionsResponse +- description: + name: ListShieldsResponse +- description: + name: ListToolGroupsResponse +- description: + name: ListToolsResponse - description: name: LogEventRequest @@ -5727,6 +5797,9 @@ tags: - description: name: QueryDocumentsResponse +- description: + name: QuerySpanTreeRequest - description: name: QuerySpansRequest @@ -5775,9 +5848,6 @@ tags: name: SafetyViolation - description: name: SamplingParams -- description: - name: SamplingStrategy - description: name: SaveSpansToDatasetRequest @@ -5898,6 +5968,12 @@ tags: /> name: ToolResponseMessage - name: ToolRuntime +- description: + name: TopKSamplingStrategy +- description: + name: TopPSamplingStrategy - description: name: Trace - description: @@ -5909,18 +5985,6 @@ tags: name: Turn - description: name: URL -- description: - name: UnregisterDatasetRequest -- description: - name: UnregisterMemoryBankRequest -- description: - name: UnregisterModelRequest -- description: - name: UnregisterToolGroupRequest - description: name: UnstructuredLogEvent @@ -6003,18 +6067,15 @@ x-tagGroups: - DataConfig - Dataset - DatasetFormat - - DeleteAgentsRequest - - DeleteAgentsSessionRequest - EfficiencyConfig - EmbeddingsRequest - EmbeddingsResponse - EvalTask - EvaluateResponse - EvaluateRowsRequest - - GetAgentsSessionRequest - - GetSpanTreeRequest - GraphMemoryBank - GraphMemoryBankParams + - GreedySamplingStrategy - HealthInfo - ImageContentItem - ImageDelta @@ -6031,7 +6092,16 @@ x-tagGroups: - KeywordMemoryBank - KeywordMemoryBankParams - LLMAsJudgeScoringFnParams - - ListRuntimeToolsRequest + - ListDatasetsResponse + - ListEvalTasksResponse + - ListMemoryBanksResponse + - ListModelsResponse + - ListPostTrainingJobsResponse + - ListProvidersResponse + - ListScoringFunctionsResponse + - ListShieldsResponse + - ListToolGroupsResponse + - ListToolsResponse - LogEventRequest - LogSeverity - LoraFinetuningConfig @@ -6057,6 +6127,7 @@ x-tagGroups: - QueryConditionOp - QueryDocumentsRequest - QueryDocumentsResponse + - QuerySpanTreeRequest - QuerySpansRequest - QueryTracesRequest - RegexParserScoringFnParams @@ -6074,7 +6145,6 @@ x-tagGroups: - RunShieldResponse - SafetyViolation - SamplingParams - - SamplingStrategy - SaveSpansToDatasetRequest - ScoreBatchRequest - ScoreBatchResponse @@ -6115,14 +6185,12 @@ x-tagGroups: - ToolPromptFormat - ToolResponse - ToolResponseMessage + - TopKSamplingStrategy + - TopPSamplingStrategy - Trace - TrainingConfig - Turn - URL - - UnregisterDatasetRequest - - UnregisterMemoryBankRequest - - UnregisterModelRequest - - UnregisterToolGroupRequest - UnstructuredLogEvent - UserMessage - VectorMemoryBank diff --git a/llama_stack/apis/agents/agents.py b/llama_stack/apis/agents/agents.py index c3f3d21f0..63d0920fb 100644 --- a/llama_stack/apis/agents/agents.py +++ b/llama_stack/apis/agents/agents.py @@ -7,6 +7,7 @@ from datetime import datetime from enum import Enum from typing import ( + Annotated, Any, AsyncIterator, Dict, @@ -20,7 +21,6 @@ from typing import ( from llama_models.schema_utils import json_schema_type, register_schema, webmethod from pydantic import BaseModel, ConfigDict, Field -from typing_extensions import Annotated from llama_stack.apis.common.content_types import ContentDelta, InterleavedContent, URL from llama_stack.apis.inference import ( @@ -296,13 +296,13 @@ class AgentStepResponse(BaseModel): @runtime_checkable @trace_protocol class Agents(Protocol): - @webmethod(route="/agents/create") + @webmethod(route="/agents", method="POST") async def create_agent( self, agent_config: AgentConfig, ) -> AgentCreateResponse: ... - @webmethod(route="/agents/turn/create") + @webmethod(route="/agents/{agent_id}/session/{session_id}/turn", method="POST") async def create_agent_turn( self, agent_id: str, @@ -318,36 +318,52 @@ class Agents(Protocol): toolgroups: Optional[List[AgentToolGroup]] = None, ) -> Union[Turn, AsyncIterator[AgentTurnResponseStreamChunk]]: ... - @webmethod(route="/agents/turn/get") + @webmethod( + route="/agents/{agent_id}/session/{session_id}/turn/{turn_id}", method="GET" + ) async def get_agents_turn( - self, agent_id: str, session_id: str, turn_id: str + self, + agent_id: str, + session_id: str, + turn_id: str, ) -> Turn: ... - @webmethod(route="/agents/step/get") + @webmethod( + route="/agents/{agent_id}/session/{session_id}/turn/{turn_id}/step/{step_id}", + method="GET", + ) async def get_agents_step( - self, agent_id: str, session_id: str, turn_id: str, step_id: str + self, + agent_id: str, + session_id: str, + turn_id: str, + step_id: str, ) -> AgentStepResponse: ... - @webmethod(route="/agents/session/create") + @webmethod(route="/agents/{agent_id}/session", method="POST") async def create_agent_session( self, agent_id: str, session_name: str, ) -> AgentSessionCreateResponse: ... - @webmethod(route="/agents/session/get") + @webmethod(route="/agents/{agent_id}/session/{session_id}", method="GET") async def get_agents_session( self, - agent_id: str, session_id: str, + agent_id: str, turn_ids: Optional[List[str]] = None, ) -> Session: ... - @webmethod(route="/agents/session/delete") - async def delete_agents_session(self, agent_id: str, session_id: str) -> None: ... + @webmethod(route="/agents/{agent_id}/session/{session_id}", method="DELETE") + async def delete_agents_session( + self, + session_id: str, + agent_id: str, + ) -> None: ... - @webmethod(route="/agents/delete") - async def delete_agents( + @webmethod(route="/agents/{agent_id}", method="DELETE") + async def delete_agent( self, agent_id: str, ) -> None: ... diff --git a/llama_stack/apis/batch_inference/batch_inference.py b/llama_stack/apis/batch_inference/batch_inference.py index 81826a7b1..ca5ba059f 100644 --- a/llama_stack/apis/batch_inference/batch_inference.py +++ b/llama_stack/apis/batch_inference/batch_inference.py @@ -54,7 +54,7 @@ class BatchChatCompletionResponse(BaseModel): @runtime_checkable class BatchInference(Protocol): - @webmethod(route="/batch-inference/completion") + @webmethod(route="/batch-inference/completion", method="POST") async def batch_completion( self, model: str, @@ -63,7 +63,7 @@ class BatchInference(Protocol): logprobs: Optional[LogProbConfig] = None, ) -> BatchCompletionResponse: ... - @webmethod(route="/batch-inference/chat-completion") + @webmethod(route="/batch-inference/chat-completion", method="POST") async def batch_chat_completion( self, model: str, diff --git a/llama_stack/apis/datasetio/datasetio.py b/llama_stack/apis/datasetio/datasetio.py index 983e0e4ea..8b4c25a1d 100644 --- a/llama_stack/apis/datasetio/datasetio.py +++ b/llama_stack/apis/datasetio/datasetio.py @@ -29,7 +29,7 @@ class DatasetIO(Protocol): # keeping for aligning with inference/safety, but this is not used dataset_store: DatasetStore - @webmethod(route="/datasetio/get-rows-paginated", method="GET") + @webmethod(route="/datasetio/rows", method="GET") async def get_rows_paginated( self, dataset_id: str, @@ -38,7 +38,7 @@ class DatasetIO(Protocol): filter_condition: Optional[str] = None, ) -> PaginatedRowsResult: ... - @webmethod(route="/datasetio/append-rows", method="POST") + @webmethod(route="/datasetio/rows", method="POST") async def append_rows( self, dataset_id: str, rows: List[Dict[str, Any]] ) -> None: ... diff --git a/llama_stack/apis/datasets/datasets.py b/llama_stack/apis/datasets/datasets.py index 7afc0f8fd..5ad5bdcdb 100644 --- a/llama_stack/apis/datasets/datasets.py +++ b/llama_stack/apis/datasets/datasets.py @@ -7,11 +7,9 @@ from typing import Any, Dict, List, Literal, Optional, Protocol from llama_models.schema_utils import json_schema_type, webmethod - from pydantic import BaseModel, Field from llama_stack.apis.common.content_types import URL - from llama_stack.apis.common.type_system import ParamType from llama_stack.apis.resource import Resource, ResourceType @@ -44,8 +42,12 @@ class DatasetInput(CommonDatasetFields, BaseModel): provider_dataset_id: Optional[str] = None +class ListDatasetsResponse(BaseModel): + data: List[Dataset] + + class Datasets(Protocol): - @webmethod(route="/datasets/register", method="POST") + @webmethod(route="/datasets", method="POST") async def register_dataset( self, dataset_id: str, @@ -56,16 +58,16 @@ class Datasets(Protocol): metadata: Optional[Dict[str, Any]] = None, ) -> None: ... - @webmethod(route="/datasets/get", method="GET") + @webmethod(route="/datasets/{dataset_id}", method="GET") async def get_dataset( self, dataset_id: str, ) -> Optional[Dataset]: ... - @webmethod(route="/datasets/list", method="GET") - async def list_datasets(self) -> List[Dataset]: ... + @webmethod(route="/datasets", method="GET") + async def list_datasets(self) -> ListDatasetsResponse: ... - @webmethod(route="/datasets/unregister", method="POST") + @webmethod(route="/datasets/{dataset_id}", method="DELETE") async def unregister_dataset( self, dataset_id: str, diff --git a/llama_stack/apis/eval/eval.py b/llama_stack/apis/eval/eval.py index 1073d6310..1b8f768ad 100644 --- a/llama_stack/apis/eval/eval.py +++ b/llama_stack/apis/eval/eval.py @@ -7,9 +7,7 @@ from typing import Any, Dict, List, Literal, Optional, Protocol, Union from llama_models.schema_utils import json_schema_type, webmethod - from pydantic import BaseModel, Field - from typing_extensions import Annotated from llama_stack.apis.agents import AgentConfig @@ -76,7 +74,7 @@ class EvaluateResponse(BaseModel): class Eval(Protocol): - @webmethod(route="/eval/run-eval", method="POST") + @webmethod(route="/eval/run", method="POST") async def run_eval( self, task_id: str, @@ -92,11 +90,11 @@ class Eval(Protocol): task_config: EvalTaskConfig, ) -> EvaluateResponse: ... - @webmethod(route="/eval/job/status", method="GET") - async def job_status(self, task_id: str, job_id: str) -> Optional[JobStatus]: ... + @webmethod(route="/eval/jobs/{job_id}", method="GET") + async def job_status(self, job_id: str, task_id: str) -> Optional[JobStatus]: ... - @webmethod(route="/eval/job/cancel", method="POST") - async def job_cancel(self, task_id: str, job_id: str) -> None: ... + @webmethod(route="/eval/jobs/cancel", method="POST") + async def job_cancel(self, job_id: str, task_id: str) -> None: ... - @webmethod(route="/eval/job/result", method="GET") - async def job_result(self, task_id: str, job_id: str) -> EvaluateResponse: ... + @webmethod(route="/eval/jobs/{job_id}/result", method="GET") + async def job_result(self, job_id: str, task_id: str) -> EvaluateResponse: ... diff --git a/llama_stack/apis/eval_tasks/eval_tasks.py b/llama_stack/apis/eval_tasks/eval_tasks.py index 083681289..a0a533055 100644 --- a/llama_stack/apis/eval_tasks/eval_tasks.py +++ b/llama_stack/apis/eval_tasks/eval_tasks.py @@ -6,7 +6,6 @@ from typing import Any, Dict, List, Literal, Optional, Protocol, runtime_checkable from llama_models.schema_utils import json_schema_type, webmethod - from pydantic import BaseModel, Field from llama_stack.apis.resource import Resource, ResourceType @@ -40,15 +39,22 @@ class EvalTaskInput(CommonEvalTaskFields, BaseModel): provider_eval_task_id: Optional[str] = None +class ListEvalTasksResponse(BaseModel): + data: List[EvalTask] + + @runtime_checkable class EvalTasks(Protocol): - @webmethod(route="/eval-tasks/list", method="GET") - async def list_eval_tasks(self) -> List[EvalTask]: ... + @webmethod(route="/eval-tasks", method="GET") + async def list_eval_tasks(self) -> ListEvalTasksResponse: ... - @webmethod(route="/eval-tasks/get", method="GET") - async def get_eval_task(self, name: str) -> Optional[EvalTask]: ... + @webmethod(route="/eval-tasks/{eval_task_id}", method="GET") + async def get_eval_task( + self, + eval_task_id: str, + ) -> Optional[EvalTask]: ... - @webmethod(route="/eval-tasks/register", method="POST") + @webmethod(route="/eval-tasks", method="POST") async def register_eval_task( self, eval_task_id: str, diff --git a/llama_stack/apis/inference/inference.py b/llama_stack/apis/inference/inference.py index b525aa331..fdda5fe1b 100644 --- a/llama_stack/apis/inference/inference.py +++ b/llama_stack/apis/inference/inference.py @@ -291,7 +291,7 @@ class ModelStore(Protocol): class Inference(Protocol): model_store: ModelStore - @webmethod(route="/inference/completion") + @webmethod(route="/inference/completion", method="POST") async def completion( self, model_id: str, @@ -302,7 +302,7 @@ class Inference(Protocol): logprobs: Optional[LogProbConfig] = None, ) -> Union[CompletionResponse, AsyncIterator[CompletionResponseStreamChunk]]: ... - @webmethod(route="/inference/chat-completion") + @webmethod(route="/inference/chat-completion", method="POST") async def chat_completion( self, model_id: str, @@ -319,7 +319,7 @@ class Inference(Protocol): ChatCompletionResponse, AsyncIterator[ChatCompletionResponseStreamChunk] ]: ... - @webmethod(route="/inference/embeddings") + @webmethod(route="/inference/embeddings", method="POST") async def embeddings( self, model_id: str, diff --git a/llama_stack/apis/inspect/inspect.py b/llama_stack/apis/inspect/inspect.py index 699bce7b7..e2bb98217 100644 --- a/llama_stack/apis/inspect/inspect.py +++ b/llama_stack/apis/inspect/inspect.py @@ -34,10 +34,14 @@ class VersionInfo(BaseModel): version: str +class ListProvidersResponse(BaseModel): + data: List[ProviderInfo] + + @runtime_checkable class Inspect(Protocol): @webmethod(route="/providers/list", method="GET") - async def list_providers(self) -> Dict[str, ProviderInfo]: ... + async def list_providers(self) -> ListProvidersResponse: ... @webmethod(route="/routes/list", method="GET") async def list_routes(self) -> Dict[str, List[RouteInfo]]: ... diff --git a/llama_stack/apis/memory/memory.py b/llama_stack/apis/memory/memory.py index 8096a107a..6e6fcf697 100644 --- a/llama_stack/apis/memory/memory.py +++ b/llama_stack/apis/memory/memory.py @@ -50,7 +50,7 @@ class Memory(Protocol): # this will just block now until documents are inserted, but it should # probably return a Job instance which can be polled for completion - @webmethod(route="/memory/insert") + @webmethod(route="/memory/insert", method="POST") async def insert_documents( self, bank_id: str, @@ -58,7 +58,7 @@ class Memory(Protocol): ttl_seconds: Optional[int] = None, ) -> None: ... - @webmethod(route="/memory/query") + @webmethod(route="/memory/query", method="POST") async def query_documents( self, bank_id: str, diff --git a/llama_stack/apis/memory_banks/memory_banks.py b/llama_stack/apis/memory_banks/memory_banks.py index 21569beff..ec8ba824b 100644 --- a/llama_stack/apis/memory_banks/memory_banks.py +++ b/llama_stack/apis/memory_banks/memory_banks.py @@ -16,7 +16,6 @@ from typing import ( ) from llama_models.schema_utils import json_schema_type, register_schema, webmethod - from pydantic import BaseModel, Field from llama_stack.apis.resource import Resource, ResourceType @@ -133,16 +132,23 @@ class MemoryBankInput(BaseModel): provider_memory_bank_id: Optional[str] = None +class ListMemoryBanksResponse(BaseModel): + data: List[MemoryBank] + + @runtime_checkable @trace_protocol class MemoryBanks(Protocol): - @webmethod(route="/memory-banks/list", method="GET") - async def list_memory_banks(self) -> List[MemoryBank]: ... + @webmethod(route="/memory-banks", method="GET") + async def list_memory_banks(self) -> ListMemoryBanksResponse: ... - @webmethod(route="/memory-banks/get", method="GET") - async def get_memory_bank(self, memory_bank_id: str) -> Optional[MemoryBank]: ... + @webmethod(route="/memory-banks/{memory_bank_id}", method="GET") + async def get_memory_bank( + self, + memory_bank_id: str, + ) -> Optional[MemoryBank]: ... - @webmethod(route="/memory-banks/register", method="POST") + @webmethod(route="/memory-banks", method="POST") async def register_memory_bank( self, memory_bank_id: str, @@ -151,5 +157,5 @@ class MemoryBanks(Protocol): provider_memory_bank_id: Optional[str] = None, ) -> MemoryBank: ... - @webmethod(route="/memory-banks/unregister", method="POST") + @webmethod(route="/memory-banks/{memory_bank_id}", method="DELETE") async def unregister_memory_bank(self, memory_bank_id: str) -> None: ... diff --git a/llama_stack/apis/models/models.py b/llama_stack/apis/models/models.py index 0ee23ecc1..3361c2836 100644 --- a/llama_stack/apis/models/models.py +++ b/llama_stack/apis/models/models.py @@ -52,16 +52,23 @@ class ModelInput(CommonModelFields): model_config = ConfigDict(protected_namespaces=()) +class ListModelsResponse(BaseModel): + data: List[Model] + + @runtime_checkable @trace_protocol class Models(Protocol): - @webmethod(route="/models/list", method="GET") - async def list_models(self) -> List[Model]: ... + @webmethod(route="/models", method="GET") + async def list_models(self) -> ListModelsResponse: ... - @webmethod(route="/models/get", method="GET") - async def get_model(self, identifier: str) -> Optional[Model]: ... + @webmethod(route="/models/{model_id}", method="GET") + async def get_model( + self, + model_id: str, + ) -> Optional[Model]: ... - @webmethod(route="/models/register", method="POST") + @webmethod(route="/models", method="POST") async def register_model( self, model_id: str, @@ -71,5 +78,8 @@ class Models(Protocol): model_type: Optional[ModelType] = None, ) -> Model: ... - @webmethod(route="/models/unregister", method="POST") - async def unregister_model(self, model_id: str) -> None: ... + @webmethod(route="/models/{model_id}", method="DELETE") + async def unregister_model( + self, + model_id: str, + ) -> None: ... diff --git a/llama_stack/apis/post_training/post_training.py b/llama_stack/apis/post_training/post_training.py index 8841dc1d0..b9aa3bbde 100644 --- a/llama_stack/apis/post_training/post_training.py +++ b/llama_stack/apis/post_training/post_training.py @@ -6,16 +6,13 @@ from datetime import datetime from enum import Enum - from typing import Any, Dict, List, Literal, Optional, Protocol, Union from llama_models.schema_utils import json_schema_type, webmethod - from pydantic import BaseModel, Field from typing_extensions import Annotated from llama_stack.apis.common.content_types import URL - from llama_stack.apis.common.job_types import JobStatus from llama_stack.apis.common.training_types import Checkpoint @@ -159,6 +156,10 @@ class PostTrainingJobStatusResponse(BaseModel): checkpoints: List[Checkpoint] = Field(default_factory=list) +class ListPostTrainingJobsResponse(BaseModel): + data: List[PostTrainingJob] + + @json_schema_type class PostTrainingJobArtifactsResponse(BaseModel): """Artifacts of a finetuning job.""" @@ -197,7 +198,7 @@ class PostTraining(Protocol): ) -> PostTrainingJob: ... @webmethod(route="/post-training/jobs", method="GET") - async def get_training_jobs(self) -> List[PostTrainingJob]: ... + async def get_training_jobs(self) -> ListPostTrainingJobsResponse: ... @webmethod(route="/post-training/job/status", method="GET") async def get_training_job_status( diff --git a/llama_stack/apis/safety/safety.py b/llama_stack/apis/safety/safety.py index dd24642b1..513733d1e 100644 --- a/llama_stack/apis/safety/safety.py +++ b/llama_stack/apis/safety/safety.py @@ -12,7 +12,6 @@ from pydantic import BaseModel, Field from llama_stack.apis.inference import Message from llama_stack.apis.shields import Shield - from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol @@ -49,7 +48,7 @@ class ShieldStore(Protocol): class Safety(Protocol): shield_store: ShieldStore - @webmethod(route="/safety/run-shield") + @webmethod(route="/safety/run-shield", method="POST") async def run_shield( self, shield_id: str, diff --git a/llama_stack/apis/scoring/scoring.py b/llama_stack/apis/scoring/scoring.py index 996291dcc..5bacaaf66 100644 --- a/llama_stack/apis/scoring/scoring.py +++ b/llama_stack/apis/scoring/scoring.py @@ -11,7 +11,6 @@ from pydantic import BaseModel from llama_stack.apis.scoring_functions import ScoringFn, ScoringFnParams - # mapping of metric to value ScoringResultRow = Dict[str, Any] @@ -43,7 +42,7 @@ class ScoringFunctionStore(Protocol): class Scoring(Protocol): scoring_function_store: ScoringFunctionStore - @webmethod(route="/scoring/score-batch") + @webmethod(route="/scoring/score-batch", method="POST") async def score_batch( self, dataset_id: str, @@ -51,7 +50,7 @@ class Scoring(Protocol): save_results_dataset: bool = False, ) -> ScoreBatchResponse: ... - @webmethod(route="/scoring/score") + @webmethod(route="/scoring/score", method="POST") async def score( self, input_rows: List[Dict[str, Any]], diff --git a/llama_stack/apis/scoring_functions/scoring_functions.py b/llama_stack/apis/scoring_functions/scoring_functions.py index fc57cfbbf..3089dc0a4 100644 --- a/llama_stack/apis/scoring_functions/scoring_functions.py +++ b/llama_stack/apis/scoring_functions/scoring_functions.py @@ -21,7 +21,6 @@ from pydantic import BaseModel, Field from typing_extensions import Annotated from llama_stack.apis.common.type_system import ParamType - from llama_stack.apis.resource import Resource, ResourceType @@ -129,15 +128,21 @@ class ScoringFnInput(CommonScoringFnFields, BaseModel): provider_scoring_fn_id: Optional[str] = None +class ListScoringFunctionsResponse(BaseModel): + data: List[ScoringFn] + + @runtime_checkable class ScoringFunctions(Protocol): - @webmethod(route="/scoring-functions/list", method="GET") - async def list_scoring_functions(self) -> List[ScoringFn]: ... + @webmethod(route="/scoring-functions", method="GET") + async def list_scoring_functions(self) -> ListScoringFunctionsResponse: ... - @webmethod(route="/scoring-functions/get", method="GET") - async def get_scoring_function(self, scoring_fn_id: str) -> Optional[ScoringFn]: ... + @webmethod(route="/scoring-functions/{scoring_fn_id}", method="GET") + async def get_scoring_function( + self, scoring_fn_id: str, / + ) -> Optional[ScoringFn]: ... - @webmethod(route="/scoring-functions/register", method="POST") + @webmethod(route="/scoring-functions", method="POST") async def register_scoring_function( self, scoring_fn_id: str, diff --git a/llama_stack/apis/shields/shields.py b/llama_stack/apis/shields/shields.py index 8d4d5f9fd..3dd685b14 100644 --- a/llama_stack/apis/shields/shields.py +++ b/llama_stack/apis/shields/shields.py @@ -38,16 +38,20 @@ class ShieldInput(CommonShieldFields): provider_shield_id: Optional[str] = None +class ListShieldsResponse(BaseModel): + data: List[Shield] + + @runtime_checkable @trace_protocol class Shields(Protocol): - @webmethod(route="/shields/list", method="GET") - async def list_shields(self) -> List[Shield]: ... + @webmethod(route="/shields", method="GET") + async def list_shields(self) -> ListShieldsResponse: ... - @webmethod(route="/shields/get", method="GET") + @webmethod(route="/shields/{identifier}", method="GET") async def get_shield(self, identifier: str) -> Optional[Shield]: ... - @webmethod(route="/shields/register", method="POST") + @webmethod(route="/shields", method="POST") async def register_shield( self, shield_id: str, diff --git a/llama_stack/apis/telemetry/telemetry.py b/llama_stack/apis/telemetry/telemetry.py index 23a475bff..d04cb67e3 100644 --- a/llama_stack/apis/telemetry/telemetry.py +++ b/llama_stack/apis/telemetry/telemetry.py @@ -185,8 +185,8 @@ class Telemetry(Protocol): order_by: Optional[List[str]] = None, ) -> List[Trace]: ... - @webmethod(route="/telemetry/get-span-tree", method="POST") - async def get_span_tree( + @webmethod(route="/telemetry/query-span-tree", method="POST") + async def query_span_tree( self, span_id: str, attributes_to_return: Optional[List[str]] = None, diff --git a/llama_stack/apis/tools/tools.py b/llama_stack/apis/tools/tools.py index d2bdf9873..fb990cc41 100644 --- a/llama_stack/apis/tools/tools.py +++ b/llama_stack/apis/tools/tools.py @@ -74,13 +74,21 @@ class ToolInvocationResult(BaseModel): class ToolStore(Protocol): def get_tool(self, tool_name: str) -> Tool: ... - def get_tool_group(self, tool_group_id: str) -> ToolGroup: ... + def get_tool_group(self, toolgroup_id: str) -> ToolGroup: ... + + +class ListToolGroupsResponse(BaseModel): + data: List[ToolGroup] + + +class ListToolsResponse(BaseModel): + data: List[Tool] @runtime_checkable @trace_protocol class ToolGroups(Protocol): - @webmethod(route="/toolgroups/register", method="POST") + @webmethod(route="/toolgroups", method="POST") async def register_tool_group( self, toolgroup_id: str, @@ -91,27 +99,33 @@ class ToolGroups(Protocol): """Register a tool group""" ... - @webmethod(route="/toolgroups/get", method="GET") + @webmethod(route="/toolgroups/{toolgroup_id}", method="GET") async def get_tool_group( self, toolgroup_id: str, ) -> ToolGroup: ... - @webmethod(route="/toolgroups/list", method="GET") - async def list_tool_groups(self) -> List[ToolGroup]: + @webmethod(route="/toolgroups", method="GET") + async def list_tool_groups(self) -> ListToolGroupsResponse: """List tool groups with optional provider""" ... - @webmethod(route="/tools/list", method="GET") - async def list_tools(self, tool_group_id: Optional[str] = None) -> List[Tool]: + @webmethod(route="/tools", method="GET") + async def list_tools(self, toolgroup_id: Optional[str] = None) -> ListToolsResponse: """List tools with optional tool group""" ... - @webmethod(route="/tools/get", method="GET") - async def get_tool(self, tool_name: str) -> Tool: ... + @webmethod(route="/tools/{tool_name}", method="GET") + async def get_tool( + self, + tool_name: str, + ) -> Tool: ... - @webmethod(route="/toolgroups/unregister", method="POST") - async def unregister_tool_group(self, tool_group_id: str) -> None: + @webmethod(route="/toolgroups/{toolgroup_id}", method="DELETE") + async def unregister_toolgroup( + self, + toolgroup_id: str, + ) -> None: """Unregister a tool group""" ... diff --git a/llama_stack/distribution/routers/routing_tables.py b/llama_stack/distribution/routers/routing_tables.py index a3a64bf6b..e02606936 100644 --- a/llama_stack/distribution/routers/routing_tables.py +++ b/llama_stack/distribution/routers/routing_tables.py @@ -10,23 +10,32 @@ from pydantic import TypeAdapter from llama_stack.apis.common.content_types import URL from llama_stack.apis.common.type_system import ParamType -from llama_stack.apis.datasets import Dataset, Datasets -from llama_stack.apis.eval_tasks import EvalTask, EvalTasks +from llama_stack.apis.datasets import Dataset, Datasets, ListDatasetsResponse +from llama_stack.apis.eval_tasks import EvalTask, EvalTasks, ListEvalTasksResponse from llama_stack.apis.memory_banks import ( BankParams, + ListMemoryBanksResponse, MemoryBank, MemoryBanks, MemoryBankType, ) -from llama_stack.apis.models import Model, Models, ModelType +from llama_stack.apis.models import ListModelsResponse, Model, Models, ModelType from llama_stack.apis.resource import ResourceType from llama_stack.apis.scoring_functions import ( + ListScoringFunctionsResponse, ScoringFn, ScoringFnParams, ScoringFunctions, ) -from llama_stack.apis.shields import Shield, Shields -from llama_stack.apis.tools import Tool, ToolGroup, ToolGroups, ToolHost +from llama_stack.apis.shields import ListShieldsResponse, Shield, Shields +from llama_stack.apis.tools import ( + ListToolGroupsResponse, + ListToolsResponse, + Tool, + ToolGroup, + ToolGroups, + ToolHost, +) from llama_stack.distribution.datatypes import ( RoutableObject, RoutableObjectWithProvider, @@ -215,11 +224,11 @@ class CommonRoutingTableImpl(RoutingTable): class ModelsRoutingTable(CommonRoutingTableImpl, Models): - async def list_models(self) -> List[Model]: - return await self.get_all_with_type("model") + async def list_models(self) -> ListModelsResponse: + return ListModelsResponse(data=await self.get_all_with_type("model")) - async def get_model(self, identifier: str) -> Optional[Model]: - return await self.get_object_by_identifier("model", identifier) + async def get_model(self, model_id: str) -> Optional[Model]: + return await self.get_object_by_identifier("model", model_id) async def register_model( self, @@ -265,8 +274,10 @@ class ModelsRoutingTable(CommonRoutingTableImpl, Models): class ShieldsRoutingTable(CommonRoutingTableImpl, Shields): - async def list_shields(self) -> List[Shield]: - return await self.get_all_with_type(ResourceType.shield.value) + async def list_shields(self) -> ListShieldsResponse: + return ListShieldsResponse( + data=await self.get_all_with_type(ResourceType.shield.value) + ) async def get_shield(self, identifier: str) -> Optional[Shield]: return await self.get_object_by_identifier("shield", identifier) @@ -301,8 +312,8 @@ class ShieldsRoutingTable(CommonRoutingTableImpl, Shields): class MemoryBanksRoutingTable(CommonRoutingTableImpl, MemoryBanks): - async def list_memory_banks(self) -> List[MemoryBank]: - return await self.get_all_with_type(ResourceType.memory_bank.value) + async def list_memory_banks(self) -> ListMemoryBanksResponse: + return ListMemoryBanksResponse(data=await self.get_all_with_type("memory_bank")) async def get_memory_bank(self, memory_bank_id: str) -> Optional[MemoryBank]: return await self.get_object_by_identifier("memory_bank", memory_bank_id) @@ -365,8 +376,10 @@ class MemoryBanksRoutingTable(CommonRoutingTableImpl, MemoryBanks): class DatasetsRoutingTable(CommonRoutingTableImpl, Datasets): - async def list_datasets(self) -> List[Dataset]: - return await self.get_all_with_type(ResourceType.dataset.value) + async def list_datasets(self) -> ListDatasetsResponse: + return ListDatasetsResponse( + data=await self.get_all_with_type(ResourceType.dataset.value) + ) async def get_dataset(self, dataset_id: str) -> Optional[Dataset]: return await self.get_object_by_identifier("dataset", dataset_id) @@ -410,8 +423,10 @@ class DatasetsRoutingTable(CommonRoutingTableImpl, Datasets): class ScoringFunctionsRoutingTable(CommonRoutingTableImpl, ScoringFunctions): - async def list_scoring_functions(self) -> List[ScoringFn]: - return await self.get_all_with_type(ResourceType.scoring_function.value) + async def list_scoring_functions(self) -> ListScoringFunctionsResponse: + return ListScoringFunctionsResponse( + data=await self.get_all_with_type(ResourceType.scoring_function.value) + ) async def get_scoring_function(self, scoring_fn_id: str) -> Optional[ScoringFn]: return await self.get_object_by_identifier("scoring_function", scoring_fn_id) @@ -447,11 +462,11 @@ class ScoringFunctionsRoutingTable(CommonRoutingTableImpl, ScoringFunctions): class EvalTasksRoutingTable(CommonRoutingTableImpl, EvalTasks): - async def list_eval_tasks(self) -> List[EvalTask]: - return await self.get_all_with_type(ResourceType.eval_task.value) + async def list_eval_tasks(self) -> ListEvalTasksResponse: + return ListEvalTasksResponse(data=await self.get_all_with_type("eval_task")) - async def get_eval_task(self, name: str) -> Optional[EvalTask]: - return await self.get_object_by_identifier("eval_task", name) + async def get_eval_task(self, eval_task_id: str) -> Optional[EvalTask]: + return await self.get_object_by_identifier("eval_task", eval_task_id) async def register_eval_task( self, @@ -485,14 +500,14 @@ class EvalTasksRoutingTable(CommonRoutingTableImpl, EvalTasks): class ToolGroupsRoutingTable(CommonRoutingTableImpl, ToolGroups): - async def list_tools(self, tool_group_id: Optional[str] = None) -> List[Tool]: + async def list_tools(self, toolgroup_id: Optional[str] = None) -> ListToolsResponse: tools = await self.get_all_with_type("tool") - if tool_group_id: - tools = [tool for tool in tools if tool.toolgroup_id == tool_group_id] - return tools + if toolgroup_id: + tools = [tool for tool in tools if tool.toolgroup_id == toolgroup_id] + return ListToolsResponse(data=tools) - async def list_tool_groups(self) -> List[ToolGroup]: - return await self.get_all_with_type("tool_group") + async def list_tool_groups(self) -> ListToolGroupsResponse: + return ListToolGroupsResponse(data=await self.get_all_with_type("tool_group")) async def get_tool_group(self, toolgroup_id: str) -> ToolGroup: return await self.get_object_by_identifier("tool_group", toolgroup_id) @@ -551,11 +566,11 @@ class ToolGroupsRoutingTable(CommonRoutingTableImpl, ToolGroups): ) ) - async def unregister_tool_group(self, tool_group_id: str) -> None: - tool_group = await self.get_tool_group(tool_group_id) + async def unregister_toolgroup(self, toolgroup_id: str) -> None: + tool_group = await self.get_tool_group(toolgroup_id) if tool_group is None: - raise ValueError(f"Tool group {tool_group_id} not found") - tools = await self.list_tools(tool_group_id) + raise ValueError(f"Tool group {toolgroup_id} not found") + tools = await self.list_tools(toolgroup_id).data for tool in tools: await self.unregister_object(tool) await self.unregister_object(tool_group) diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py index 34334de77..2d216d314 100644 --- a/llama_stack/distribution/server/server.py +++ b/llama_stack/distribution/server/server.py @@ -14,16 +14,13 @@ import signal import sys import traceback import warnings - from contextlib import asynccontextmanager - from importlib.metadata import version as parse_version from pathlib import Path -from typing import Any, Union +from typing import Any, List, Union import yaml - -from fastapi import Body, FastAPI, HTTPException, Request +from fastapi import Body, FastAPI, HTTPException, Path as FastapiPath, Request from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse, StreamingResponse from pydantic import BaseModel, ValidationError @@ -31,7 +28,6 @@ from termcolor import cprint from typing_extensions import Annotated from llama_stack.distribution.datatypes import StackRunConfig - from llama_stack.distribution.distribution import builtin_automatically_routed_apis from llama_stack.distribution.request_headers import set_request_provider_data from llama_stack.distribution.resolver import InvalidProviderError @@ -41,13 +37,11 @@ from llama_stack.distribution.stack import ( replace_env_vars, validate_env_pair, ) - from llama_stack.providers.datatypes import Api from llama_stack.providers.inline.telemetry.meta_reference.config import TelemetryConfig from llama_stack.providers.inline.telemetry.meta_reference.telemetry import ( TelemetryAdapter, ) - from llama_stack.providers.utils.telemetry.tracing import ( end_trace, setup_logger, @@ -56,7 +50,6 @@ from llama_stack.providers.utils.telemetry.tracing import ( from .endpoints import get_all_api_endpoints - REPO_ROOT = Path(__file__).parent.parent.parent.parent @@ -178,7 +171,7 @@ async def sse_generator(event_gen): ) -def create_dynamic_typed_route(func: Any, method: str): +def create_dynamic_typed_route(func: Any, method: str, route: str): async def endpoint(request: Request, **kwargs): set_request_provider_data(request.headers) @@ -196,6 +189,7 @@ def create_dynamic_typed_route(func: Any, method: str): raise translate_exception(e) from e sig = inspect.signature(func) + new_params = [ inspect.Parameter( "request", inspect.Parameter.POSITIONAL_OR_KEYWORD, annotation=Request @@ -203,12 +197,21 @@ def create_dynamic_typed_route(func: Any, method: str): ] new_params.extend(sig.parameters.values()) + path_params = extract_path_params(route) if method == "post": - # make sure every parameter is annotated with Body() so FASTAPI doesn't - # do anything too intelligent and ask for some parameters in the query - # and some in the body + # Annotate parameters that are in the path with Path(...) and others with Body(...) new_params = [new_params[0]] + [ - param.replace(annotation=Annotated[param.annotation, Body(..., embed=True)]) + ( + param.replace( + annotation=Annotated[ + param.annotation, FastapiPath(..., title=param.name) + ] + ) + if param.name in path_params + else param.replace( + annotation=Annotated[param.annotation, Body(..., embed=True)] + ) + ) for param in new_params[1:] ] @@ -386,6 +389,7 @@ def main(): create_dynamic_typed_route( impl_method, endpoint.method, + endpoint.route, ) ) @@ -409,5 +413,13 @@ def main(): uvicorn.run(app, host=listen_host, port=args.port) +def extract_path_params(route: str) -> List[str]: + segments = route.split("/") + params = [ + seg[1:-1] for seg in segments if seg.startswith("{") and seg.endswith("}") + ] + return params + + if __name__ == "__main__": main() diff --git a/llama_stack/distribution/stack.py b/llama_stack/distribution/stack.py index e3edf1e16..ad7bcd234 100644 --- a/llama_stack/distribution/stack.py +++ b/llama_stack/distribution/stack.py @@ -93,7 +93,11 @@ async def register_resources(run_config: StackRunConfig, impls: Dict[Api, Any]): await method(**obj.model_dump()) method = getattr(impls[api], list_method) - for obj in await method(): + response = await method() + + objects_to_process = response.data if hasattr(response, "data") else response + + for obj in objects_to_process: log.info( f"{rsrc.capitalize()}: {colored(obj.identifier, 'white', attrs=['bold'])} served by {colored(obj.provider_id, 'white', attrs=['bold'])}", ) diff --git a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py index 2299e80d1..2ebc7ded1 100644 --- a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py +++ b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py @@ -624,6 +624,10 @@ class ChatAgent(ShieldRunnerMixin): step_type=StepType.tool_execution.value, step_id=step_id, tool_call=tool_call, + delta=ToolCallDelta( + parse_status=ToolCallParseStatus.in_progress, + content=tool_call, + ), ) ) ) @@ -735,8 +739,8 @@ class ChatAgent(ShieldRunnerMixin): for toolgroup_name in agent_config_toolgroups: if toolgroup_name not in toolgroups_for_turn_set: continue - tools = await self.tool_groups_api.list_tools(tool_group_id=toolgroup_name) - for tool_def in tools: + tools = await self.tool_groups_api.list_tools(toolgroup_id=toolgroup_name) + for tool_def in tools.data: if ( toolgroup_name.startswith("builtin") and toolgroup_name != MEMORY_GROUP diff --git a/llama_stack/providers/inline/agents/meta_reference/agents.py b/llama_stack/providers/inline/agents/meta_reference/agents.py index faff716ce..d22ef82ab 100644 --- a/llama_stack/providers/inline/agents/meta_reference/agents.py +++ b/llama_stack/providers/inline/agents/meta_reference/agents.py @@ -223,5 +223,5 @@ class MetaReferenceAgentsImpl(Agents): async def delete_agents_session(self, agent_id: str, session_id: str) -> None: await self.persistence_store.delete(f"session:{agent_id}:{session_id}") - async def delete_agents(self, agent_id: str) -> None: + async def delete_agent(self, agent_id: str) -> None: await self.persistence_store.delete(f"agent:{agent_id}") diff --git a/llama_stack/providers/inline/post_training/torchtune/post_training.py b/llama_stack/providers/inline/post_training/torchtune/post_training.py index 90fbf7026..4abe13de2 100644 --- a/llama_stack/providers/inline/post_training/torchtune/post_training.py +++ b/llama_stack/providers/inline/post_training/torchtune/post_training.py @@ -4,7 +4,7 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. from datetime import datetime -from typing import Any, Dict, List, Optional +from typing import Any, Dict, Optional from llama_models.schema_utils import webmethod @@ -14,6 +14,7 @@ from llama_stack.apis.post_training import ( AlgorithmConfig, DPOAlignmentConfig, JobStatus, + ListPostTrainingJobsResponse, LoraFinetuningConfig, PostTrainingJob, PostTrainingJobArtifactsResponse, @@ -114,8 +115,8 @@ class TorchtunePostTrainingImpl: logger_config: Dict[str, Any], ) -> PostTrainingJob: ... - async def get_training_jobs(self) -> List[PostTrainingJob]: - return self.jobs_list + async def get_training_jobs(self) -> ListPostTrainingJobsResponse: + return ListPostTrainingJobsResponse(data=self.jobs_list) @webmethod(route="/post-training/job/status") async def get_training_job_status( diff --git a/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py b/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py index 332a150cf..e2e318375 100644 --- a/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py +++ b/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py @@ -249,7 +249,7 @@ class TelemetryAdapter(TelemetryDatasetMixin, Telemetry): order_by=order_by, ) - async def get_span_tree( + async def query_span_tree( self, span_id: str, attributes_to_return: Optional[List[str]] = None, diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py index 19a4064a0..747b64dd1 100644 --- a/tests/client-sdk/agents/test_agents.py +++ b/tests/client-sdk/agents/test_agents.py @@ -83,13 +83,13 @@ class TestClientTool(ClientTool): def agent_config(llama_stack_client): available_models = [ model.identifier - for model in llama_stack_client.models.list() + for model in llama_stack_client.models.list().data if model.identifier.startswith("meta-llama") and "405" not in model.identifier ] model_id = available_models[0] print(f"Using model: {model_id}") available_shields = [ - shield.identifier for shield in llama_stack_client.shields.list() + shield.identifier for shield in llama_stack_client.shields.list().data ] available_shields = available_shields[:1] print(f"Using shield: {available_shields}") From b3202bcf77b00e3a30b50c1aa45c19470de295a6 Mon Sep 17 00:00:00 2001 From: cdgamarose-nv Date: Thu, 16 Jan 2025 03:34:43 +0530 Subject: [PATCH 017/107] add nvidia distribution (#565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # What does this PR do? adds nvidia template for creating a distribution using inference adapter for NVIDIA NIMs. ## Test Plan Please describe: Build llama stack distribution for nvidia using the template, docker and conda. ```bash (.venv) local-cdgamarose@a4u8g-0006:~/llama-stack$ llama-stack-client configure --endpoint http://localhost:5000 Done! You can now use the Llama Stack Client CLI with endpoint http://localhost:5000 (.venv) local-cdgamarose@a4u8g-0006:~/llama-stack$ llama-stack-client models list ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┓ ┃ identifier ┃ provider_id ┃ provider_resource_id ┃ metadata ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━┩ │ Llama3.1-8B-Instruct │ nvidia │ meta/llama-3.1-8b-instruct │ {} │ │ meta-llama/Llama-3.2-3B-Instruct │ nvidia │ meta/llama-3.2-3b-instruct │ {} │ └──────────────────────────────────┴─────────────┴────────────────────────────┴──────────┘ (.venv) local-cdgamarose@a4u8g-0006:~/llama-stack$ llama-stack-client inference chat-completion --message "hello, write me a 2 sentence poem" ChatCompletionResponse( completion_message=CompletionMessage( content='Here is a 2 sentence poem:\n\nThe sun sets slow and paints the sky, \nA gentle hue of pink that makes me sigh.', role='assistant', stop_reason='end_of_turn', tool_calls=[] ), logprobs=None ) ``` ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [x] Ran pre-commit to handle lint / formatting issues. - [x] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [x] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --------- Co-authored-by: Matthew Farrellee --- distributions/inline-nvidia/build.yaml | 1 + distributions/inline-nvidia/compose.yaml | 58 ++++++++++ distributions/inline-nvidia/run.yaml | 100 ++++++++++++++++++ distributions/remote-nvidia/build.yaml | 1 + distributions/remote-nvidia/compose.yaml | 19 ++++ distributions/remote-nvidia/run.yaml | 1 + docs/source/distributions/index.md | 1 + .../remote_hosted_distro/nvidia.md | 65 ++++++++++++ .../self_hosted_distro/nvidia.md | 60 +++++++++++ .../remote/inference/nvidia/config.py | 9 +- llama_stack/templates/nvidia/__init__.py | 7 ++ llama_stack/templates/nvidia/build.yaml | 30 ++++++ llama_stack/templates/nvidia/doc_template.md | 61 +++++++++++ llama_stack/templates/nvidia/nvidia.py | 70 ++++++++++++ llama_stack/templates/nvidia/run.yaml | 100 ++++++++++++++++++ 15 files changed, 582 insertions(+), 1 deletion(-) create mode 120000 distributions/inline-nvidia/build.yaml create mode 100644 distributions/inline-nvidia/compose.yaml create mode 100644 distributions/inline-nvidia/run.yaml create mode 120000 distributions/remote-nvidia/build.yaml create mode 100644 distributions/remote-nvidia/compose.yaml create mode 120000 distributions/remote-nvidia/run.yaml create mode 100644 docs/source/distributions/remote_hosted_distro/nvidia.md create mode 100644 docs/source/distributions/self_hosted_distro/nvidia.md create mode 100644 llama_stack/templates/nvidia/__init__.py create mode 100644 llama_stack/templates/nvidia/build.yaml create mode 100644 llama_stack/templates/nvidia/doc_template.md create mode 100644 llama_stack/templates/nvidia/nvidia.py create mode 100644 llama_stack/templates/nvidia/run.yaml diff --git a/distributions/inline-nvidia/build.yaml b/distributions/inline-nvidia/build.yaml new file mode 120000 index 000000000..8903d2e57 --- /dev/null +++ b/distributions/inline-nvidia/build.yaml @@ -0,0 +1 @@ +../../llama_stack/templates/nvidia/build.yaml \ No newline at end of file diff --git a/distributions/inline-nvidia/compose.yaml b/distributions/inline-nvidia/compose.yaml new file mode 100644 index 000000000..644b7d23d --- /dev/null +++ b/distributions/inline-nvidia/compose.yaml @@ -0,0 +1,58 @@ +services: + nim: + image: ${DOCKER_IMAGE:-nvcr.io/nim/meta/llama-3.1-8b-instruct:latest} + network_mode: "host" + volumes: + - nim-llm-cache:/opt/nim/.cache + ports: + - "8000:8000" + shm_size: 16G + environment: + - CUDA_VISIBLE_DEVICES=0 + - NIM_HTTP_API_PORT=8000 + - NIM_TRITON_LOG_VERBOSE=1 + - NGC_API_KEY=${NIM_NGC_API_KEY:-${NGC_API_KEY:-ngcapikey}} + command: [] + deploy: + resources: + reservations: + devices: + - driver: nvidia + # that's the closest analogue to --gpus; provide + # an integer amount of devices or 'all' + count: 1 + # Devices are reserved using a list of capabilities, making + # capabilities the only required field. A device MUST + # satisfy all the requested capabilities for a successful + # reservation. + capabilities: [gpu] + runtime: nvidia + healthcheck: + test: ["CMD", "curl", "http://localhost:8000/v1/health/ready"] + interval: 5s + timeout: 5s + retries: 30 + start_period: 120s + llamastack: + depends_on: + - nim + image: distribution-nvidia:dev + network_mode: "host" + volumes: + - ~/.llama:/root/.llama + - ./run.yaml:/root/llamastack-run-nvidia.yaml + ports: + - "5000:5000" + environment: + - INFERENCE_MODEL=${INFERENCE_MODEL:-Llama3.1-8B-Instruct} + - NVIDIA_API_KEY=${NVIDIA_API_KEY:-} + entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml-config /root/llamastack-run-nvidia.yaml" + deploy: + restart_policy: + condition: on-failure + delay: 3s + max_attempts: 5 + window: 60s +volumes: + nim-llm-cache: + driver: local diff --git a/distributions/inline-nvidia/run.yaml b/distributions/inline-nvidia/run.yaml new file mode 100644 index 000000000..e96a0429c --- /dev/null +++ b/distributions/inline-nvidia/run.yaml @@ -0,0 +1,100 @@ +version: '2' +image_name: nvidia +conda_env: nvidia +apis: +- agents +- datasetio +- eval +- inference +- memory +- safety +- scoring +- telemetry +- tool_runtime +providers: + inference: + - provider_id: nvidia + provider_type: remote::nvidia + config: + url: http://localhost:8000 + api_key: ${env.NVIDIA_API_KEY} # TODO: don't need api key, code adjustments needed + memory: + - provider_id: faiss + provider_type: inline::faiss + config: + kvstore: + type: sqlite + namespace: null + db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/faiss_store.db + safety: + - provider_id: llama-guard + provider_type: inline::llama-guard + config: {} + agents: + - provider_id: meta-reference + provider_type: inline::meta-reference + config: + persistence_store: + type: sqlite + namespace: null + db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/agents_store.db + telemetry: + - provider_id: meta-reference + provider_type: inline::meta-reference + config: + service_name: ${env.OTEL_SERVICE_NAME:llama-stack} + sinks: ${env.TELEMETRY_SINKS:console,sqlite} + sqlite_db_path: ${env.SQLITE_DB_PATH:~/.llama/distributions/nvidia/trace_store.db} + eval: + - provider_id: meta-reference + provider_type: inline::meta-reference + config: {} + datasetio: + - provider_id: huggingface + provider_type: remote::huggingface + config: {} + - provider_id: localfs + provider_type: inline::localfs + config: {} + scoring: + - provider_id: basic + provider_type: inline::basic + config: {} + - provider_id: llm-as-judge + provider_type: inline::llm-as-judge + config: {} + - provider_id: braintrust + provider_type: inline::braintrust + config: + openai_api_key: ${env.OPENAI_API_KEY:} + tool_runtime: + - provider_id: brave-search + provider_type: remote::brave-search + config: + api_key: ${env.BRAVE_SEARCH_API_KEY:} + max_results: 3 + - provider_id: tavily-search + provider_type: remote::tavily-search + config: + api_key: ${env.TAVILY_SEARCH_API_KEY:} + max_results: 3 + - provider_id: code-interpreter + provider_type: inline::code-interpreter + config: {} + - provider_id: memory-runtime + provider_type: inline::memory-runtime + config: {} +metadata_store: + type: sqlite + db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/registry.db +models: +- metadata: {} + model_id: ${env.INFERENCE_MODEL} + provider_id: nvidia + model_type: llm +shields: [] +memory_banks: [] +datasets: [] +scoring_fns: [] +eval_tasks: [] +tool_groups: [] diff --git a/distributions/remote-nvidia/build.yaml b/distributions/remote-nvidia/build.yaml new file mode 120000 index 000000000..8903d2e57 --- /dev/null +++ b/distributions/remote-nvidia/build.yaml @@ -0,0 +1 @@ +../../llama_stack/templates/nvidia/build.yaml \ No newline at end of file diff --git a/distributions/remote-nvidia/compose.yaml b/distributions/remote-nvidia/compose.yaml new file mode 100644 index 000000000..04b12d0da --- /dev/null +++ b/distributions/remote-nvidia/compose.yaml @@ -0,0 +1,19 @@ +services: + llamastack: + image: distribution-nvidia:dev + network_mode: "host" + volumes: + - ~/.llama:/root/.llama + - ./run.yaml:/root/llamastack-run-nvidia.yaml + ports: + - "5000:5000" + environment: + - INFERENCE_MODEL=${INFERENCE_MODEL:-Llama3.1-8B-Instruct} + - NVIDIA_API_KEY=${NVIDIA_API_KEY:-} + entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml-config /root/llamastack-run-nvidia.yaml" + deploy: + restart_policy: + condition: on-failure + delay: 3s + max_attempts: 5 + window: 60s diff --git a/distributions/remote-nvidia/run.yaml b/distributions/remote-nvidia/run.yaml new file mode 120000 index 000000000..85da3e26b --- /dev/null +++ b/distributions/remote-nvidia/run.yaml @@ -0,0 +1 @@ +../../llama_stack/templates/nvidia/run.yaml \ No newline at end of file diff --git a/docs/source/distributions/index.md b/docs/source/distributions/index.md index 9b2f46869..5d84ebd9e 100644 --- a/docs/source/distributions/index.md +++ b/docs/source/distributions/index.md @@ -20,6 +20,7 @@ If so, we suggest: - {dockerhub}`distribution-remote-vllm` ([Guide](self_hosted_distro/remote-vllm)) - {dockerhub}`distribution-meta-reference-gpu` ([Guide](self_hosted_distro/meta-reference-gpu)) - {dockerhub}`distribution-tgi` ([Guide](self_hosted_distro/tgi)) + - {dockerhub} `distribution-nvidia` ([Guide](self_hosted_distro/nvidia)) - **Are you running on a "regular" desktop machine?** If so, we suggest: diff --git a/docs/source/distributions/remote_hosted_distro/nvidia.md b/docs/source/distributions/remote_hosted_distro/nvidia.md new file mode 100644 index 000000000..874bb8bb2 --- /dev/null +++ b/docs/source/distributions/remote_hosted_distro/nvidia.md @@ -0,0 +1,65 @@ +# NVIDIA Distribution + +The `llamastack/distribution-nvidia` distribution consists of the following provider configurations. + +| API | Provider(s) | +|-----|-------------| +| agents | `inline::meta-reference` | +| datasetio | `remote::huggingface`, `inline::localfs` | +| eval | `inline::meta-reference` | +| inference | `remote::nvidia` | +| memory | `inline::faiss` | +| safety | `inline::llama-guard` | +| scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | +| telemetry | `inline::meta-reference` | +| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` | + + +### Environment Variables + +The following environment variables can be configured: + +- `LLAMASTACK_PORT`: Port for the Llama Stack distribution server (default: `5001`) +- `NVIDIA_API_KEY`: NVIDIA API Key (default: ``) + +### Models + +The following models are available by default: + +- `${env.INFERENCE_MODEL} (None)` + + +### Prerequisite: API Keys + +Make sure you have access to a NVIDIA API Key. You can get one by visiting [https://build.nvidia.com/](https://build.nvidia.com/). + + +## Running Llama Stack with NVIDIA + +You can do this via Conda (build code) or Docker which has a pre-built image. + +### Via Docker + +This method allows you to get started quickly without having to build the distribution code. + +```bash +LLAMA_STACK_PORT=5001 +docker run \ + -it \ + -p $LLAMA_STACK_PORT:$LLAMA_STACK_PORT \ + -v ./run.yaml:/root/my-run.yaml \ + llamastack/distribution-nvidia \ + --yaml-config /root/my-run.yaml \ + --port $LLAMA_STACK_PORT \ + --env NVIDIA_API_KEY=$NVIDIA_API_KEY +``` + +### Via Conda + +```bash +llama stack build --template nvidia --image-type conda +llama stack run ./run.yaml \ + --port 5001 \ + --env NVIDIA_API_KEY=$NVIDIA_API_KEY + --env INFERENCE=$INFERENCE_MODEL +``` diff --git a/docs/source/distributions/self_hosted_distro/nvidia.md b/docs/source/distributions/self_hosted_distro/nvidia.md new file mode 100644 index 000000000..b86d950dd --- /dev/null +++ b/docs/source/distributions/self_hosted_distro/nvidia.md @@ -0,0 +1,60 @@ +# NVIDIA Distribution + +The `llamastack/distribution-nvidia` distribution consists of the following provider configurations. + +| API | Provider(s) | +|-----|-------------| +| agents | `inline::meta-reference` | +| inference | `remote::nvidia` | +| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | +| safety | `inline::llama-guard` | +| telemetry | `inline::meta-reference` | + + +### Environment Variables + +The following environment variables can be configured: + +- `LLAMASTACK_PORT`: Port for the Llama Stack distribution server (default: `5001`) +- `NVIDIA_API_KEY`: NVIDIA API Key (default: ``) + +### Models + +The following models are available by default: + +- `${env.INFERENCE_MODEL} (None)` + + +### Prerequisite: API Keys + +Make sure you have access to a NVIDIA API Key. You can get one by visiting [https://build.nvidia.com/](https://build.nvidia.com/). + + +## Running Llama Stack with NVIDIA + +You can do this via Conda (build code) or Docker which has a pre-built image. + +### Via Docker + +This method allows you to get started quickly without having to build the distribution code. + +```bash +LLAMA_STACK_PORT=5001 +docker run \ + -it \ + -p $LLAMA_STACK_PORT:$LLAMA_STACK_PORT \ + -v ./run.yaml:/root/my-run.yaml \ + llamastack/distribution-nvidia \ + --yaml-config /root/my-run.yaml \ + --port $LLAMA_STACK_PORT \ + --env NVIDIA_API_KEY=$NVIDIA_API_KEY +``` + +### Via Conda + +```bash +llama stack build --template nvidia --image-type conda +llama stack run ./run.yaml \ + --port 5001 \ + --env NVIDIA_API_KEY=$NVIDIA_API_KEY +``` diff --git a/llama_stack/providers/remote/inference/nvidia/config.py b/llama_stack/providers/remote/inference/nvidia/config.py index 9e81211bd..d062e65d2 100644 --- a/llama_stack/providers/remote/inference/nvidia/config.py +++ b/llama_stack/providers/remote/inference/nvidia/config.py @@ -5,7 +5,7 @@ # the root directory of this source tree. import os -from typing import Optional +from typing import Any, Dict, Optional from llama_models.schema_utils import json_schema_type from pydantic import BaseModel, Field, SecretStr @@ -48,3 +48,10 @@ class NVIDIAConfig(BaseModel): default=60, description="Timeout for the HTTP requests", ) + + @classmethod + def sample_run_config(cls, **kwargs) -> Dict[str, Any]: + return { + "url": "https://integrate.api.nvidia.com", + "api_key": "${env.NVIDIA_API_KEY}", + } diff --git a/llama_stack/templates/nvidia/__init__.py b/llama_stack/templates/nvidia/__init__.py new file mode 100644 index 000000000..24e2fbd21 --- /dev/null +++ b/llama_stack/templates/nvidia/__init__.py @@ -0,0 +1,7 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +from .nvidia import get_distribution_template # noqa: F401 diff --git a/llama_stack/templates/nvidia/build.yaml b/llama_stack/templates/nvidia/build.yaml new file mode 100644 index 000000000..813502ada --- /dev/null +++ b/llama_stack/templates/nvidia/build.yaml @@ -0,0 +1,30 @@ +version: '2' +name: nvidia +distribution_spec: + description: Use NVIDIA NIM for running LLM inference + providers: + inference: + - remote::nvidia + memory: + - inline::faiss + safety: + - inline::llama-guard + agents: + - inline::meta-reference + telemetry: + - inline::meta-reference + eval: + - inline::meta-reference + datasetio: + - remote::huggingface + - inline::localfs + scoring: + - inline::basic + - inline::llm-as-judge + - inline::braintrust + tool_runtime: + - remote::brave-search + - remote::tavily-search + - inline::code-interpreter + - inline::memory-runtime +image_type: conda diff --git a/llama_stack/templates/nvidia/doc_template.md b/llama_stack/templates/nvidia/doc_template.md new file mode 100644 index 000000000..9d9006a27 --- /dev/null +++ b/llama_stack/templates/nvidia/doc_template.md @@ -0,0 +1,61 @@ +# NVIDIA Distribution + +The `llamastack/distribution-{{ name }}` distribution consists of the following provider configurations. + +{{ providers_table }} + +{% if run_config_env_vars %} +### Environment Variables + +The following environment variables can be configured: + +{% for var, (default_value, description) in run_config_env_vars.items() %} +- `{{ var }}`: {{ description }} (default: `{{ default_value }}`) +{% endfor %} +{% endif %} + +{% if default_models %} +### Models + +The following models are available by default: + +{% for model in default_models %} +- `{{ model.model_id }} ({{ model.provider_model_id }})` +{% endfor %} +{% endif %} + + +### Prerequisite: API Keys + +Make sure you have access to a NVIDIA API Key. You can get one by visiting [https://build.nvidia.com/](https://build.nvidia.com/). + + +## Running Llama Stack with NVIDIA + +You can do this via Conda (build code) or Docker which has a pre-built image. + +### Via Docker + +This method allows you to get started quickly without having to build the distribution code. + +```bash +LLAMA_STACK_PORT=5001 +docker run \ + -it \ + -p $LLAMA_STACK_PORT:$LLAMA_STACK_PORT \ + -v ./run.yaml:/root/my-run.yaml \ + llamastack/distribution-{{ name }} \ + --yaml-config /root/my-run.yaml \ + --port $LLAMA_STACK_PORT \ + --env NVIDIA_API_KEY=$NVIDIA_API_KEY +``` + +### Via Conda + +```bash +llama stack build --template nvidia --image-type conda +llama stack run ./run.yaml \ + --port 5001 \ + --env NVIDIA_API_KEY=$NVIDIA_API_KEY + --env INFERENCE_MODEL=$INFERENCE_MODEL +``` diff --git a/llama_stack/templates/nvidia/nvidia.py b/llama_stack/templates/nvidia/nvidia.py new file mode 100644 index 000000000..173db2d7f --- /dev/null +++ b/llama_stack/templates/nvidia/nvidia.py @@ -0,0 +1,70 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +from pathlib import Path + +from llama_stack.distribution.datatypes import ModelInput, Provider +from llama_stack.providers.remote.inference.nvidia import NVIDIAConfig + +from llama_stack.templates.template import DistributionTemplate, RunConfigSettings + + +def get_distribution_template() -> DistributionTemplate: + providers = { + "inference": ["remote::nvidia"], + "memory": ["inline::faiss"], + "safety": ["inline::llama-guard"], + "agents": ["inline::meta-reference"], + "telemetry": ["inline::meta-reference"], + "eval": ["inline::meta-reference"], + "datasetio": ["remote::huggingface", "inline::localfs"], + "scoring": ["inline::basic", "inline::llm-as-judge", "inline::braintrust"], + "tool_runtime": [ + "remote::brave-search", + "remote::tavily-search", + "inline::code-interpreter", + "inline::memory-runtime", + ], + } + + inference_provider = Provider( + provider_id="nvidia", + provider_type="remote::nvidia", + config=NVIDIAConfig.sample_run_config(), + ) + + inference_model = ModelInput( + model_id="${env.INFERENCE_MODEL}", + provider_id="nvidia", + ) + + return DistributionTemplate( + name="nvidia", + distro_type="remote_hosted", + description="Use NVIDIA NIM for running LLM inference", + docker_image=None, + template_path=Path(__file__).parent / "doc_template.md", + providers=providers, + default_models=[inference_model], + run_configs={ + "run.yaml": RunConfigSettings( + provider_overrides={ + "inference": [inference_provider], + }, + default_models=[inference_model], + ), + }, + run_config_env_vars={ + "LLAMASTACK_PORT": ( + "5001", + "Port for the Llama Stack distribution server", + ), + "NVIDIA_API_KEY": ( + "", + "NVIDIA API Key", + ), + }, + ) diff --git a/llama_stack/templates/nvidia/run.yaml b/llama_stack/templates/nvidia/run.yaml new file mode 100644 index 000000000..84b0437ba --- /dev/null +++ b/llama_stack/templates/nvidia/run.yaml @@ -0,0 +1,100 @@ +version: '2' +image_name: nvidia +conda_env: nvidia +apis: +- agents +- datasetio +- eval +- inference +- memory +- safety +- scoring +- telemetry +- tool_runtime +providers: + inference: + - provider_id: nvidia + provider_type: remote::nvidia + config: + url: https://integrate.api.nvidia.com + api_key: ${env.NVIDIA_API_KEY} + memory: + - provider_id: faiss + provider_type: inline::faiss + config: + kvstore: + type: sqlite + namespace: null + db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/faiss_store.db + safety: + - provider_id: llama-guard + provider_type: inline::llama-guard + config: {} + agents: + - provider_id: meta-reference + provider_type: inline::meta-reference + config: + persistence_store: + type: sqlite + namespace: null + db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/agents_store.db + telemetry: + - provider_id: meta-reference + provider_type: inline::meta-reference + config: + service_name: ${env.OTEL_SERVICE_NAME:llama-stack} + sinks: ${env.TELEMETRY_SINKS:console,sqlite} + sqlite_db_path: ${env.SQLITE_DB_PATH:~/.llama/distributions/nvidia/trace_store.db} + eval: + - provider_id: meta-reference + provider_type: inline::meta-reference + config: {} + datasetio: + - provider_id: huggingface + provider_type: remote::huggingface + config: {} + - provider_id: localfs + provider_type: inline::localfs + config: {} + scoring: + - provider_id: basic + provider_type: inline::basic + config: {} + - provider_id: llm-as-judge + provider_type: inline::llm-as-judge + config: {} + - provider_id: braintrust + provider_type: inline::braintrust + config: + openai_api_key: ${env.OPENAI_API_KEY:} + tool_runtime: + - provider_id: brave-search + provider_type: remote::brave-search + config: + api_key: ${env.BRAVE_SEARCH_API_KEY:} + max_results: 3 + - provider_id: tavily-search + provider_type: remote::tavily-search + config: + api_key: ${env.TAVILY_SEARCH_API_KEY:} + max_results: 3 + - provider_id: code-interpreter + provider_type: inline::code-interpreter + config: {} + - provider_id: memory-runtime + provider_type: inline::memory-runtime + config: {} +metadata_store: + type: sqlite + db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/registry.db +models: +- metadata: {} + model_id: ${env.INFERENCE_MODEL} + provider_id: nvidia + model_type: llm +shields: [] +memory_banks: [] +datasets: [] +scoring_fns: [] +eval_tasks: [] +tool_groups: [] From 27e07b44b557c8a37ff24c865b407faa33b67d76 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Wed, 15 Jan 2025 14:15:56 -0800 Subject: [PATCH 018/107] remove inline-nvidia templates --- distributions/inline-nvidia/build.yaml | 1 - distributions/inline-nvidia/compose.yaml | 58 ------------- distributions/inline-nvidia/run.yaml | 100 ----------------------- 3 files changed, 159 deletions(-) delete mode 120000 distributions/inline-nvidia/build.yaml delete mode 100644 distributions/inline-nvidia/compose.yaml delete mode 100644 distributions/inline-nvidia/run.yaml diff --git a/distributions/inline-nvidia/build.yaml b/distributions/inline-nvidia/build.yaml deleted file mode 120000 index 8903d2e57..000000000 --- a/distributions/inline-nvidia/build.yaml +++ /dev/null @@ -1 +0,0 @@ -../../llama_stack/templates/nvidia/build.yaml \ No newline at end of file diff --git a/distributions/inline-nvidia/compose.yaml b/distributions/inline-nvidia/compose.yaml deleted file mode 100644 index 644b7d23d..000000000 --- a/distributions/inline-nvidia/compose.yaml +++ /dev/null @@ -1,58 +0,0 @@ -services: - nim: - image: ${DOCKER_IMAGE:-nvcr.io/nim/meta/llama-3.1-8b-instruct:latest} - network_mode: "host" - volumes: - - nim-llm-cache:/opt/nim/.cache - ports: - - "8000:8000" - shm_size: 16G - environment: - - CUDA_VISIBLE_DEVICES=0 - - NIM_HTTP_API_PORT=8000 - - NIM_TRITON_LOG_VERBOSE=1 - - NGC_API_KEY=${NIM_NGC_API_KEY:-${NGC_API_KEY:-ngcapikey}} - command: [] - deploy: - resources: - reservations: - devices: - - driver: nvidia - # that's the closest analogue to --gpus; provide - # an integer amount of devices or 'all' - count: 1 - # Devices are reserved using a list of capabilities, making - # capabilities the only required field. A device MUST - # satisfy all the requested capabilities for a successful - # reservation. - capabilities: [gpu] - runtime: nvidia - healthcheck: - test: ["CMD", "curl", "http://localhost:8000/v1/health/ready"] - interval: 5s - timeout: 5s - retries: 30 - start_period: 120s - llamastack: - depends_on: - - nim - image: distribution-nvidia:dev - network_mode: "host" - volumes: - - ~/.llama:/root/.llama - - ./run.yaml:/root/llamastack-run-nvidia.yaml - ports: - - "5000:5000" - environment: - - INFERENCE_MODEL=${INFERENCE_MODEL:-Llama3.1-8B-Instruct} - - NVIDIA_API_KEY=${NVIDIA_API_KEY:-} - entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml-config /root/llamastack-run-nvidia.yaml" - deploy: - restart_policy: - condition: on-failure - delay: 3s - max_attempts: 5 - window: 60s -volumes: - nim-llm-cache: - driver: local diff --git a/distributions/inline-nvidia/run.yaml b/distributions/inline-nvidia/run.yaml deleted file mode 100644 index e96a0429c..000000000 --- a/distributions/inline-nvidia/run.yaml +++ /dev/null @@ -1,100 +0,0 @@ -version: '2' -image_name: nvidia -conda_env: nvidia -apis: -- agents -- datasetio -- eval -- inference -- memory -- safety -- scoring -- telemetry -- tool_runtime -providers: - inference: - - provider_id: nvidia - provider_type: remote::nvidia - config: - url: http://localhost:8000 - api_key: ${env.NVIDIA_API_KEY} # TODO: don't need api key, code adjustments needed - memory: - - provider_id: faiss - provider_type: inline::faiss - config: - kvstore: - type: sqlite - namespace: null - db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/faiss_store.db - safety: - - provider_id: llama-guard - provider_type: inline::llama-guard - config: {} - agents: - - provider_id: meta-reference - provider_type: inline::meta-reference - config: - persistence_store: - type: sqlite - namespace: null - db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/agents_store.db - telemetry: - - provider_id: meta-reference - provider_type: inline::meta-reference - config: - service_name: ${env.OTEL_SERVICE_NAME:llama-stack} - sinks: ${env.TELEMETRY_SINKS:console,sqlite} - sqlite_db_path: ${env.SQLITE_DB_PATH:~/.llama/distributions/nvidia/trace_store.db} - eval: - - provider_id: meta-reference - provider_type: inline::meta-reference - config: {} - datasetio: - - provider_id: huggingface - provider_type: remote::huggingface - config: {} - - provider_id: localfs - provider_type: inline::localfs - config: {} - scoring: - - provider_id: basic - provider_type: inline::basic - config: {} - - provider_id: llm-as-judge - provider_type: inline::llm-as-judge - config: {} - - provider_id: braintrust - provider_type: inline::braintrust - config: - openai_api_key: ${env.OPENAI_API_KEY:} - tool_runtime: - - provider_id: brave-search - provider_type: remote::brave-search - config: - api_key: ${env.BRAVE_SEARCH_API_KEY:} - max_results: 3 - - provider_id: tavily-search - provider_type: remote::tavily-search - config: - api_key: ${env.TAVILY_SEARCH_API_KEY:} - max_results: 3 - - provider_id: code-interpreter - provider_type: inline::code-interpreter - config: {} - - provider_id: memory-runtime - provider_type: inline::memory-runtime - config: {} -metadata_store: - type: sqlite - db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/registry.db -models: -- metadata: {} - model_id: ${env.INFERENCE_MODEL} - provider_id: nvidia - model_type: llm -shields: [] -memory_banks: [] -datasets: [] -scoring_fns: [] -eval_tasks: [] -tool_groups: [] From 67450e4024fe82709bbfbb0f90734fbc55d7941f Mon Sep 17 00:00:00 2001 From: Sixian Yi Date: Wed, 15 Jan 2025 15:39:05 -0800 Subject: [PATCH 019/107] bug fixes on inference tests (#774) # What does this PR do? Fixes two issues on providers/test/inference - [ ] Addresses issue (#issue) ## Test Plan ### Before ``` ===================================================================================== FAILURES ===================================================================================== __________________________________ TestVisionModelInference.test_vision_chat_completion_streaming[llama_vision-fireworks][llama_vision] ___________________________________ providers/tests/inference/test_vision_inference.py:145: in test_vision_chat_completion_streaming content = "".join( E TypeError: sequence item 0: expected str instance, TextDelta found ------------------------------------------------------------------------------ Captured log teardown ------------------------------------------------------------------------------- ERROR asyncio:base_events.py:1858 Task was destroyed but it is pending! task: ()>> ============================================================================= short test summary info ============================================================================== FAILED providers/tests/inference/test_vision_inference.py::TestVisionModelInference::test_vision_chat_completion_streaming[llama_vision-fireworks] - TypeError: sequence item 0: expected str instance, TextDelta found ============================================================== 1 failed, 2 passed, 33 deselected, 7 warnings in 3.59s ============================================================== (base) sxyi@sxyi-mbp llama_stack % ``` ### After ``` (base) sxyi@sxyi-mbp llama_stack % pytest -k "fireworks" /Users/sxyi/llama-stack/llama_stack/providers/tests/inference/test_vision_inference.py /Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/pytest_asyncio/plugin.py:208: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset. The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session" warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET)) =============================================================================== test session starts ================================================================================ platform darwin -- Python 3.13.0, pytest-8.3.3, pluggy-1.5.0 rootdir: /Users/sxyi/llama-stack configfile: pyproject.toml plugins: asyncio-0.24.0, html-4.1.1, metadata-3.1.1, dependency-0.6.0, anyio-4.6.2.post1 asyncio: mode=Mode.STRICT, default_loop_scope=None collected 36 items / 33 deselected / 3 selected providers/tests/inference/test_vision_inference.py ... [100%] =================================================================== 3 passed, 33 deselected, 7 warnings in 3.75s =================================================================== (base) sxyi@sxyi-mbp llama_stack % ``` ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- llama_stack/providers/tests/inference/test_embeddings.py | 3 ++- llama_stack/providers/tests/inference/test_vision_inference.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/llama_stack/providers/tests/inference/test_embeddings.py b/llama_stack/providers/tests/inference/test_embeddings.py index bf09896c1..ca0276ed6 100644 --- a/llama_stack/providers/tests/inference/test_embeddings.py +++ b/llama_stack/providers/tests/inference/test_embeddings.py @@ -6,7 +6,8 @@ import pytest -from llama_stack.apis.inference import EmbeddingsResponse, ModelType +from llama_stack.apis.inference import EmbeddingsResponse +from llama_stack.apis.models import ModelType # How to run this test: # pytest -v -s llama_stack/providers/tests/inference/test_embeddings.py diff --git a/llama_stack/providers/tests/inference/test_vision_inference.py b/llama_stack/providers/tests/inference/test_vision_inference.py index 1bdee051f..df2f3cfb9 100644 --- a/llama_stack/providers/tests/inference/test_vision_inference.py +++ b/llama_stack/providers/tests/inference/test_vision_inference.py @@ -143,7 +143,7 @@ class TestVisionModelInference: assert len(grouped[ChatCompletionResponseEventType.complete]) == 1 content = "".join( - chunk.event.delta + chunk.event.delta.text for chunk in grouped[ChatCompletionResponseEventType.progress] ) for expected_string in expected_strings: From 3e518c049a2e2410d4500cb19c0d3e5d51f49f08 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Wed, 15 Jan 2025 15:52:26 -0800 Subject: [PATCH 020/107] [bugfix] fix inference sdk test for v1 (#775) # What does this PR do? - fixes client sdk tests ## Test Plan ``` LLAMA_STACK_BASE_URL="http://localhost:5000" pytest -v tests/client-sdk/inference/test_inference.py ``` image ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- tests/client-sdk/inference/test_inference.py | 38 +++++++++----------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py index a50dba3a0..999f3f0e5 100644 --- a/tests/client-sdk/inference/test_inference.py +++ b/tests/client-sdk/inference/test_inference.py @@ -28,19 +28,18 @@ def provider_tool_format(inference_provider_type): @pytest.fixture(scope="session") def inference_provider_type(llama_stack_client): providers = llama_stack_client.providers.list() - if "inference" not in providers: - pytest.fail("No inference providers available") - assert len(providers["inference"]) > 0 - return providers["inference"][0].provider_type + assert len(providers.inference) > 0 + return providers.inference[0]["provider_type"] @pytest.fixture(scope="session") def text_model_id(llama_stack_client): available_models = [ model.identifier - for model in llama_stack_client.models.list() + for model in llama_stack_client.models.list().data if model.identifier.startswith("meta-llama") and "405" not in model.identifier ] + print(available_models) assert len(available_models) > 0 return available_models[0] @@ -49,7 +48,7 @@ def text_model_id(llama_stack_client): def vision_model_id(llama_stack_client): available_models = [ model.identifier - for model in llama_stack_client.models.list() + for model in llama_stack_client.models.list().data if "vision" in model.identifier.lower() ] if len(available_models) == 0: @@ -245,19 +244,13 @@ def test_text_chat_completion_with_tool_calling_and_non_streaming( # The returned tool inovcation content will be a string so it's easy to comapare with expected value # e.g. "[get_weather, {'location': 'San Francisco, CA'}]" def extract_tool_invocation_content(response): - text_content: str = "" tool_invocation_content: str = "" for chunk in response: delta = chunk.event.delta - if delta.type == "text": - text_content += delta.text - elif delta.type == "tool_call": - if isinstance(delta.content, str): - tool_invocation_content += delta.content - else: - call = delta.content - tool_invocation_content += f"[{call.tool_name}, {call.arguments}]" - return text_content, tool_invocation_content + if delta.type == "tool_call" and delta.parse_status == "succeeded": + call = delta.content + tool_invocation_content += f"[{call.tool_name}, {call.arguments}]" + return tool_invocation_content def test_text_chat_completion_with_tool_calling_and_streaming( @@ -274,8 +267,11 @@ def test_text_chat_completion_with_tool_calling_and_streaming( tool_prompt_format=provider_tool_format, stream=True, ) - text_content, tool_invocation_content = extract_tool_invocation_content(response) - + tool_invocation_content = extract_tool_invocation_content(response) + print( + "!!!!tool_invocation_content", + tool_invocation_content, + ) assert tool_invocation_content == "[get_weather, {'location': 'San Francisco, CA'}]" @@ -362,8 +358,8 @@ def test_image_chat_completion_streaming(llama_stack_client, vision_model_id): messages=[message], stream=True, ) - streamed_content = [ - str(chunk.event.delta.text.lower().strip()) for chunk in response - ] + streamed_content = "" + for chunk in response: + streamed_content += chunk.event.delta.text.lower() assert len(streamed_content) > 0 assert any(expected in streamed_content for expected in {"dog", "puppy", "pup"}) From 8fd9bcb8cd05c72f2a232e5a34bc0b5891a5b2e2 Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Wed, 15 Jan 2025 15:59:45 -0800 Subject: [PATCH 021/107] fix routing in library client (#776) # What does this PR do? Library client needs to match the impl based on both the path and method. Since path is no longer static, this PR uses the inefficient way of using regexes computed based on the annotated route path to match against the incoming request path. The variables now also can come to the impl from both path or the body, which is also handled cleanly by finding all the regex matches. ## Test Plan LLAMA_STACK_CONFIG="/Users/dineshyv/.llama/distributions/llamastack-together/together-run.yaml" pytest -v tests/client-sdk/agents/test_agents.py --- llama_stack/distribution/library_client.py | 62 +++++++++++++++++----- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/llama_stack/distribution/library_client.py b/llama_stack/distribution/library_client.py index 0c124e64b..fdc68c0a4 100644 --- a/llama_stack/distribution/library_client.py +++ b/llama_stack/distribution/library_client.py @@ -9,6 +9,7 @@ import inspect import json import logging import os +import re from concurrent.futures import ThreadPoolExecutor from enum import Enum from pathlib import Path @@ -232,13 +233,23 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): endpoints = get_all_api_endpoints() endpoint_impls = {} + + def _convert_path_to_regex(path: str) -> str: + # Convert {param} to named capture groups + pattern = re.sub(r"{(\w+)}", r"(?P<\1>[^/]+)", path) + return f"^{pattern}$" + for api, api_endpoints in endpoints.items(): if api not in self.impls: continue for endpoint in api_endpoints: impl = self.impls[api] func = getattr(impl, endpoint.name) - endpoint_impls[endpoint.route] = func + if endpoint.method not in endpoint_impls: + endpoint_impls[endpoint.method] = {} + endpoint_impls[endpoint.method][ + _convert_path_to_regex(endpoint.route) + ] = func self.endpoint_impls = endpoint_impls return True @@ -273,6 +284,32 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): await end_trace() return response + def _find_matching_endpoint(self, method: str, path: str) -> tuple[Any, dict]: + """Find the matching endpoint implementation for a given method and path. + + Args: + method: HTTP method (GET, POST, etc.) + path: URL path to match against + + Returns: + A tuple of (endpoint_function, path_params) + + Raises: + ValueError: If no matching endpoint is found + """ + impls = self.endpoint_impls.get(method) + if not impls: + raise ValueError(f"No endpoint found for {path}") + + for regex, func in impls.items(): + match = re.match(regex, path) + if match: + # Extract named groups from the regex match + path_params = match.groupdict() + return func, path_params + + raise ValueError(f"No endpoint found for {path}") + async def _call_non_streaming( self, *, @@ -280,15 +317,13 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): options: Any, ): path = options.url - body = options.params or {} body |= options.json_data or {} - func = self.endpoint_impls.get(path) - if not func: - raise ValueError(f"No endpoint found for {path}") - body = self._convert_body(path, body) - result = await func(**body) + matched_func, path_params = self._find_matching_endpoint(options.method, path) + body |= path_params + body = self._convert_body(path, options.method, body) + result = await matched_func(**body) json_content = json.dumps(convert_pydantic_to_json_value(result)) mock_response = httpx.Response( @@ -325,11 +360,10 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): path = options.url body = options.params or {} body |= options.json_data or {} - func = self.endpoint_impls.get(path) - if not func: - raise ValueError(f"No endpoint found for {path}") + func, path_params = self._find_matching_endpoint(options.method, path) + body |= path_params - body = self._convert_body(path, body) + body = self._convert_body(path, options.method, body) async def gen(): async for chunk in await func(**body): @@ -367,11 +401,13 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): ) return await response.parse() - def _convert_body(self, path: str, body: Optional[dict] = None) -> dict: + def _convert_body( + self, path: str, method: str, body: Optional[dict] = None + ) -> dict: if not body: return {} - func = self.endpoint_impls[path] + func, _ = self._find_matching_endpoint(method, path) sig = inspect.signature(func) # Strip NOT_GIVENs to use the defaults in signature From 965644ce68b69c1681d4138aa5cdc15ff0c23204 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Wed, 15 Jan 2025 16:06:57 -0800 Subject: [PATCH 022/107] [bugfix] fix client-sdk tests for v1 (#777) # What does this PR do? - as title, as API have been updated ## Test Plan ``` LLAMA_STACK_BASE_URL="http://localhost:5000" pytest -v tests/client-sdk/ ``` ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- tests/client-sdk/inference/test_inference.py | 5 ---- tests/client-sdk/memory/test_memory.py | 28 ++++++++++++-------- tests/client-sdk/safety/test_safety.py | 4 +-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py index 999f3f0e5..5191a3f7f 100644 --- a/tests/client-sdk/inference/test_inference.py +++ b/tests/client-sdk/inference/test_inference.py @@ -39,7 +39,6 @@ def text_model_id(llama_stack_client): for model in llama_stack_client.models.list().data if model.identifier.startswith("meta-llama") and "405" not in model.identifier ] - print(available_models) assert len(available_models) > 0 return available_models[0] @@ -268,10 +267,6 @@ def test_text_chat_completion_with_tool_calling_and_streaming( stream=True, ) tool_invocation_content = extract_tool_invocation_content(response) - print( - "!!!!tool_invocation_content", - tool_invocation_content, - ) assert tool_invocation_content == "[get_weather, {'location': 'San Francisco, CA'}]" diff --git a/tests/client-sdk/memory/test_memory.py b/tests/client-sdk/memory/test_memory.py index 998c30125..a5f154fda 100644 --- a/tests/client-sdk/memory/test_memory.py +++ b/tests/client-sdk/memory/test_memory.py @@ -15,7 +15,8 @@ from llama_stack_client.types.memory_insert_params import Document @pytest.fixture(scope="function") def empty_memory_bank_registry(llama_stack_client): memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + memory_bank.identifier + for memory_bank in llama_stack_client.memory_banks.list().data ] for memory_bank_id in memory_banks: llama_stack_client.memory_banks.unregister(memory_bank_id=memory_bank_id) @@ -35,7 +36,8 @@ def single_entry_memory_bank_registry(llama_stack_client, empty_memory_bank_regi provider_id="faiss", ) memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + memory_bank.identifier + for memory_bank in llama_stack_client.memory_banks.list().data ] return memory_banks @@ -104,7 +106,8 @@ def test_memory_bank_retrieve(llama_stack_client, empty_memory_bank_registry): def test_memory_bank_list(llama_stack_client, empty_memory_bank_registry): memory_banks_after_register = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + memory_bank.identifier + for memory_bank in llama_stack_client.memory_banks.list().data ] assert len(memory_banks_after_register) == 0 @@ -124,14 +127,16 @@ def test_memory_bank_register(llama_stack_client, empty_memory_bank_registry): ) memory_banks_after_register = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + memory_bank.identifier + for memory_bank in llama_stack_client.memory_banks.list().data ] assert memory_banks_after_register == [memory_bank_id] def test_memory_bank_unregister(llama_stack_client, single_entry_memory_bank_registry): memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + memory_bank.identifier + for memory_bank in llama_stack_client.memory_banks.list().data ] assert len(memory_banks) == 1 @@ -139,7 +144,8 @@ def test_memory_bank_unregister(llama_stack_client, single_entry_memory_bank_reg llama_stack_client.memory_banks.unregister(memory_bank_id=memory_bank_id) memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + memory_bank.identifier + for memory_bank in llama_stack_client.memory_banks.list().data ] assert len(memory_banks) == 0 @@ -195,11 +201,10 @@ def test_memory_bank_insert_inline_and_query( def test_memory_bank_insert_from_url_and_query( llama_stack_client, empty_memory_bank_registry ): - providers = llama_stack_client.providers.list() - assert "memory" in providers - assert len(providers["memory"]) > 0 + providers = llama_stack_client.providers.list().memory + assert len(providers) > 0 - memory_provider_id = providers["memory"][0].provider_id + memory_provider_id = providers[0]["provider_id"] memory_bank_id = "test_bank" llama_stack_client.memory_banks.register( @@ -215,7 +220,8 @@ def test_memory_bank_insert_from_url_and_query( # list to check memory bank is successfully registered available_memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + memory_bank.identifier + for memory_bank in llama_stack_client.memory_banks.list().data ] assert memory_bank_id in available_memory_banks diff --git a/tests/client-sdk/safety/test_safety.py b/tests/client-sdk/safety/test_safety.py index 8eadffcfc..2d79bda5e 100644 --- a/tests/client-sdk/safety/test_safety.py +++ b/tests/client-sdk/safety/test_safety.py @@ -30,7 +30,7 @@ def data_url_from_image(file_path): @pytest.fixture(scope="session") def available_shields(llama_stack_client): - return [shield.identifier for shield in llama_stack_client.shields.list()] + return [shield.identifier for shield in llama_stack_client.shields.list().data] @pytest.fixture(scope="session") @@ -54,7 +54,7 @@ def code_scanner_shield_id(available_shields): @pytest.fixture(scope="session") def model_providers(llama_stack_client): return set( - [x.provider_id for x in llama_stack_client.providers.list()["inference"]] + [x["provider_id"] for x in llama_stack_client.providers.list().inference] ) From b76bef169c92639319dce6bab28d3d38765f8d4d Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Wed, 15 Jan 2025 18:49:36 -0800 Subject: [PATCH 023/107] fix nvidia inference provider (#781) # What does this PR do? - fixes to nvidia inference provider to account for strategy update - update nvidia templates ## Test Plan ``` llama stack run ./llama_stack/templates/nvidia/run.yaml --port 5000 LLAMA_STACK_BASE_URL="http://localhost:5000" pytest -v tests/client-sdk/inference/test_inference.py --html=report.html --self-contained-html ``` image **NOTE** - vision inference broken - tool calling broken - /completion broken cc @mattf @cdgamarose-nv for improving NVIDIA inference adapter ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- distributions/dependencies.json | 535 +++++++++--------- .../remote_hosted_distro/nvidia.md | 12 +- .../remote/inference/nvidia/openai_utils.py | 1 - llama_stack/templates/nvidia/nvidia.py | 22 +- llama_stack/templates/nvidia/run.yaml | 43 +- 5 files changed, 351 insertions(+), 262 deletions(-) diff --git a/distributions/dependencies.json b/distributions/dependencies.json index 424815419..f36b35292 100644 --- a/distributions/dependencies.json +++ b/distributions/dependencies.json @@ -1,104 +1,4 @@ { - "bedrock": [ - "aiosqlite", - "autoevals", - "blobfile", - "boto3", - "chardet", - "chromadb-client", - "datasets", - "faiss-cpu", - "fastapi", - "fire", - "httpx", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "tqdm", - "transformers", - "uvicorn", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], - "fireworks": [ - "aiosqlite", - "autoevals", - "blobfile", - "chardet", - "chromadb-client", - "datasets", - "faiss-cpu", - "fastapi", - "fire", - "fireworks-ai", - "httpx", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "tqdm", - "transformers", - "uvicorn", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], - "hf-endpoint": [ - "aiohttp", - "aiosqlite", - "autoevals", - "blobfile", - "chardet", - "chromadb-client", - "datasets", - "faiss-cpu", - "fastapi", - "fire", - "httpx", - "huggingface_hub", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "tqdm", - "transformers", - "uvicorn", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], "hf-serverless": [ "aiohttp", "aiosqlite", @@ -133,154 +33,6 @@ "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ], - "meta-reference-gpu": [ - "accelerate", - "aiosqlite", - "autoevals", - "blobfile", - "chardet", - "chromadb-client", - "datasets", - "fairscale", - "faiss-cpu", - "fastapi", - "fire", - "httpx", - "lm-format-enforcer", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentence-transformers", - "sentencepiece", - "torch", - "torchvision", - "tqdm", - "transformers", - "uvicorn", - "zmq", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], - "meta-reference-quantized-gpu": [ - "accelerate", - "aiosqlite", - "autoevals", - "blobfile", - "chardet", - "chromadb-client", - "datasets", - "fairscale", - "faiss-cpu", - "fastapi", - "fbgemm-gpu", - "fire", - "httpx", - "lm-format-enforcer", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentence-transformers", - "sentencepiece", - "torch", - "torchao==0.5.0", - "torchvision", - "tqdm", - "transformers", - "uvicorn", - "zmq", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], - "ollama": [ - "aiohttp", - "aiosqlite", - "autoevals", - "blobfile", - "chardet", - "chromadb-client", - "datasets", - "faiss-cpu", - "fastapi", - "fire", - "httpx", - "matplotlib", - "nltk", - "numpy", - "ollama", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "tqdm", - "transformers", - "uvicorn", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], - "tgi": [ - "aiohttp", - "aiosqlite", - "autoevals", - "blobfile", - "chardet", - "chromadb-client", - "datasets", - "faiss-cpu", - "fastapi", - "fire", - "httpx", - "huggingface_hub", - "matplotlib", - "nltk", - "numpy", - "openai", - "opentelemetry-exporter-otlp-proto-http", - "opentelemetry-sdk", - "pandas", - "pillow", - "psycopg2-binary", - "pypdf", - "redis", - "requests", - "scikit-learn", - "scipy", - "sentencepiece", - "tqdm", - "transformers", - "uvicorn", - "sentence-transformers --no-deps", - "torch --index-url https://download.pytorch.org/whl/cpu" - ], "together": [ "aiosqlite", "autoevals", @@ -314,11 +66,13 @@ "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ], - "cerebras": [ + "vllm-gpu": [ "aiosqlite", + "autoevals", "blobfile", - "cerebras_cloud_sdk", "chardet", + "chromadb-client", + "datasets", "faiss-cpu", "fastapi", "fire", @@ -326,6 +80,7 @@ "matplotlib", "nltk", "numpy", + "openai", "opentelemetry-exporter-otlp-proto-http", "opentelemetry-sdk", "pandas", @@ -340,6 +95,7 @@ "tqdm", "transformers", "uvicorn", + "vllm", "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ], @@ -373,7 +129,7 @@ "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ], - "vllm-gpu": [ + "fireworks": [ "aiosqlite", "autoevals", "blobfile", @@ -383,6 +139,74 @@ "faiss-cpu", "fastapi", "fire", + "fireworks-ai", + "httpx", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "tgi": [ + "aiohttp", + "aiosqlite", + "autoevals", + "blobfile", + "chardet", + "chromadb-client", + "datasets", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "huggingface_hub", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "bedrock": [ + "aiosqlite", + "autoevals", + "blobfile", + "boto3", + "chardet", + "chromadb-client", + "datasets", + "faiss-cpu", + "fastapi", + "fire", "httpx", "matplotlib", "nltk", @@ -402,7 +226,214 @@ "tqdm", "transformers", "uvicorn", - "vllm", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "meta-reference-gpu": [ + "accelerate", + "aiosqlite", + "autoevals", + "blobfile", + "chardet", + "chromadb-client", + "datasets", + "fairscale", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "lm-format-enforcer", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentence-transformers", + "sentencepiece", + "torch", + "torchvision", + "tqdm", + "transformers", + "uvicorn", + "zmq", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "nvidia": [ + "aiosqlite", + "autoevals", + "blobfile", + "chardet", + "datasets", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "meta-reference-quantized-gpu": [ + "accelerate", + "aiosqlite", + "autoevals", + "blobfile", + "chardet", + "chromadb-client", + "datasets", + "fairscale", + "faiss-cpu", + "fastapi", + "fbgemm-gpu", + "fire", + "httpx", + "lm-format-enforcer", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentence-transformers", + "sentencepiece", + "torch", + "torchao==0.5.0", + "torchvision", + "tqdm", + "transformers", + "uvicorn", + "zmq", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "cerebras": [ + "aiosqlite", + "blobfile", + "cerebras_cloud_sdk", + "chardet", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "matplotlib", + "nltk", + "numpy", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "ollama": [ + "aiohttp", + "aiosqlite", + "autoevals", + "blobfile", + "chardet", + "chromadb-client", + "datasets", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "matplotlib", + "nltk", + "numpy", + "ollama", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", + "sentence-transformers --no-deps", + "torch --index-url https://download.pytorch.org/whl/cpu" + ], + "hf-endpoint": [ + "aiohttp", + "aiosqlite", + "autoevals", + "blobfile", + "chardet", + "chromadb-client", + "datasets", + "faiss-cpu", + "fastapi", + "fire", + "httpx", + "huggingface_hub", + "matplotlib", + "nltk", + "numpy", + "openai", + "opentelemetry-exporter-otlp-proto-http", + "opentelemetry-sdk", + "pandas", + "pillow", + "psycopg2-binary", + "pypdf", + "redis", + "requests", + "scikit-learn", + "scipy", + "sentencepiece", + "tqdm", + "transformers", + "uvicorn", "sentence-transformers --no-deps", "torch --index-url https://download.pytorch.org/whl/cpu" ] diff --git a/docs/source/distributions/remote_hosted_distro/nvidia.md b/docs/source/distributions/remote_hosted_distro/nvidia.md index 874bb8bb2..7e3446863 100644 --- a/docs/source/distributions/remote_hosted_distro/nvidia.md +++ b/docs/source/distributions/remote_hosted_distro/nvidia.md @@ -26,7 +26,15 @@ The following environment variables can be configured: The following models are available by default: -- `${env.INFERENCE_MODEL} (None)` +- `meta-llama/Llama-3-8B-Instruct (meta/llama3-8b-instruct)` +- `meta-llama/Llama-3-70B-Instruct (meta/llama3-70b-instruct)` +- `meta-llama/Llama-3.1-8B-Instruct (meta/llama-3.1-8b-instruct)` +- `meta-llama/Llama-3.1-70B-Instruct (meta/llama-3.1-70b-instruct)` +- `meta-llama/Llama-3.1-405B-Instruct-FP8 (meta/llama-3.1-405b-instruct)` +- `meta-llama/Llama-3.2-1B-Instruct (meta/llama-3.2-1b-instruct)` +- `meta-llama/Llama-3.2-3B-Instruct (meta/llama-3.2-3b-instruct)` +- `meta-llama/Llama-3.2-11B-Vision-Instruct (meta/llama-3.2-11b-vision-instruct)` +- `meta-llama/Llama-3.2-90B-Vision-Instruct (meta/llama-3.2-90b-vision-instruct)` ### Prerequisite: API Keys @@ -61,5 +69,5 @@ llama stack build --template nvidia --image-type conda llama stack run ./run.yaml \ --port 5001 \ --env NVIDIA_API_KEY=$NVIDIA_API_KEY - --env INFERENCE=$INFERENCE_MODEL + --env INFERENCE_MODEL=$INFERENCE_MODEL ``` diff --git a/llama_stack/providers/remote/inference/nvidia/openai_utils.py b/llama_stack/providers/remote/inference/nvidia/openai_utils.py index 8db7f9197..e85c8dd21 100644 --- a/llama_stack/providers/remote/inference/nvidia/openai_utils.py +++ b/llama_stack/providers/remote/inference/nvidia/openai_utils.py @@ -279,7 +279,6 @@ def convert_chat_completion_request( nvext.update(top_k=strategy.top_k) elif isinstance(strategy, GreedySamplingStrategy): nvext.update(top_k=-1) - payload.update(temperature=strategy.temperature) else: raise ValueError(f"Unsupported sampling strategy: {strategy}") diff --git a/llama_stack/templates/nvidia/nvidia.py b/llama_stack/templates/nvidia/nvidia.py index 173db2d7f..9daf9c50c 100644 --- a/llama_stack/templates/nvidia/nvidia.py +++ b/llama_stack/templates/nvidia/nvidia.py @@ -6,8 +6,11 @@ from pathlib import Path +from llama_models.sku_list import all_registered_models + from llama_stack.distribution.datatypes import ModelInput, Provider from llama_stack.providers.remote.inference.nvidia import NVIDIAConfig +from llama_stack.providers.remote.inference.nvidia.nvidia import _MODEL_ALIASES from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -36,10 +39,17 @@ def get_distribution_template() -> DistributionTemplate: config=NVIDIAConfig.sample_run_config(), ) - inference_model = ModelInput( - model_id="${env.INFERENCE_MODEL}", - provider_id="nvidia", - ) + core_model_to_hf_repo = { + m.descriptor(): m.huggingface_repo for m in all_registered_models() + } + default_models = [ + ModelInput( + model_id=core_model_to_hf_repo[m.llama_model], + provider_model_id=m.provider_model_id, + provider_id="nvidia", + ) + for m in _MODEL_ALIASES + ] return DistributionTemplate( name="nvidia", @@ -48,13 +58,13 @@ def get_distribution_template() -> DistributionTemplate: docker_image=None, template_path=Path(__file__).parent / "doc_template.md", providers=providers, - default_models=[inference_model], + default_models=default_models, run_configs={ "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider], }, - default_models=[inference_model], + default_models=default_models, ), }, run_config_env_vars={ diff --git a/llama_stack/templates/nvidia/run.yaml b/llama_stack/templates/nvidia/run.yaml index 84b0437ba..d07eb25eb 100644 --- a/llama_stack/templates/nvidia/run.yaml +++ b/llama_stack/templates/nvidia/run.yaml @@ -89,8 +89,49 @@ metadata_store: db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/registry.db models: - metadata: {} - model_id: ${env.INFERENCE_MODEL} + model_id: meta-llama/Llama-3-8B-Instruct provider_id: nvidia + provider_model_id: meta/llama3-8b-instruct + model_type: llm +- metadata: {} + model_id: meta-llama/Llama-3-70B-Instruct + provider_id: nvidia + provider_model_id: meta/llama3-70b-instruct + model_type: llm +- metadata: {} + model_id: meta-llama/Llama-3.1-8B-Instruct + provider_id: nvidia + provider_model_id: meta/llama-3.1-8b-instruct + model_type: llm +- metadata: {} + model_id: meta-llama/Llama-3.1-70B-Instruct + provider_id: nvidia + provider_model_id: meta/llama-3.1-70b-instruct + model_type: llm +- metadata: {} + model_id: meta-llama/Llama-3.1-405B-Instruct-FP8 + provider_id: nvidia + provider_model_id: meta/llama-3.1-405b-instruct + model_type: llm +- metadata: {} + model_id: meta-llama/Llama-3.2-1B-Instruct + provider_id: nvidia + provider_model_id: meta/llama-3.2-1b-instruct + model_type: llm +- metadata: {} + model_id: meta-llama/Llama-3.2-3B-Instruct + provider_id: nvidia + provider_model_id: meta/llama-3.2-3b-instruct + model_type: llm +- metadata: {} + model_id: meta-llama/Llama-3.2-11B-Vision-Instruct + provider_id: nvidia + provider_model_id: meta/llama-3.2-11b-vision-instruct + model_type: llm +- metadata: {} + model_id: meta-llama/Llama-3.2-90B-Vision-Instruct + provider_id: nvidia + provider_model_id: meta/llama-3.2-90b-vision-instruct model_type: llm shields: [] memory_banks: [] From 17fd2d2fd0483ac35bb3626852a90dfb442470ba Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Wed, 15 Jan 2025 19:28:17 -0800 Subject: [PATCH 024/107] Make notebook testable (#780) # What does this PR do? This PR updates the notebook to run as a pytest by using a package called `nbval`. - [ ] Addresses issue (#issue) ## Test Plan ``` pytest -v -s --nbval-lax docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb =================================== test session starts ==================================== platform linux -- Python 3.10.16, pytest-8.3.4, pluggy-1.5.0 -- /home/hjshah/.conda/envs/nbeval/bin/python cachedir: .pytest_cache rootdir: /home/hjshah/git/llama-stack configfile: pyproject.toml plugins: nbval-0.11.0, anyio-4.8.0 collected 20 items docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 0 SKIPPED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 1 SKIPPED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 2 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 3 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 4 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 5 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 6 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 7 SKIPPED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 8 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 9 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 10 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 11 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 12 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 13 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 14 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 15 SKIPPED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 16 PASSED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 17 SKIPPED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 18 SKIPPED docs/notebooks/Llama_Stack_Building_AI_Applications::ipynb::Cell 19 PASSED ========================= 14 passed, 6 skipped in 89.69s (0:01:29) ========================= ``` --------- Co-authored-by: Hardik Shah --- ...Llama_Stack_Building_AI_Applications.ipynb | 10638 ++++++++-------- 1 file changed, 5357 insertions(+), 5281 deletions(-) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index 472e800a6..97c30a209 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -83,8 +83,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Reading package lists... Done\n", "Building dependency tree... Done\n", @@ -240,6 +240,8 @@ } ], "source": [ + "# NBVAL_SKIP\n", + "\n", "!apt-get install -y bubblewrap\n", "# install a branch of llama stack\n", "!pip install llama-stack" @@ -279,8 +281,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Requirement already satisfied: llama-stack in /usr/local/lib/python3.10/dist-packages (0.0.63)\r\n", "Requirement already satisfied: blobfile in /usr/local/lib/python3.10/dist-packages (from llama-stack) (3.0.0)\r\n", @@ -567,6 +569,8 @@ } ], "source": [ + "# NBVAL_SKIP\n", + "\n", "# This will build all the dependencies you will need\n", "!llama stack build --template together --image-type venv" ] @@ -721,15 +725,15 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Removed handler StreamHandler from root logger\n" ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "/usr/local/lib/python3.10/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning: \n", "The secret `HF_TOKEN` does not exist in your Colab secrets.\n", @@ -740,356 +744,174 @@ ] }, { - "output_type": "display_data", "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "88f0c88612bb45d59f07e93567cc0e14", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ "modules.json: 0%| | 0.00/349 [00:00Using config together:\n", "\n" + ], + "text/plain": [ + "Using config \u001b[34mtogether\u001b[0m:\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" }, { - "output_type": "display_data", "data": { - "text/plain": [ - "apis:\n", - "- agents\n", - "- datasetio\n", - "- eval\n", - "- inference\n", - "- memory\n", - "- safety\n", - "- scoring\n", - "- telemetry\n", - "- tool_runtime\n", - "conda_env: together\n", - "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "docker_image: null\n", - "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "image_name: together\n", - "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "metadata_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - "models:\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-FP8\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Meta-Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision-Turbo\n", - "- metadata:\n", - " embedding_dimension: \u001b[1;36m384\u001b[0m\n", - " model_id: all-MiniLM-L6-v2\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - embedding\n", - " provider_id: sentence-transformers\n", - " provider_model_id: null\n", - "providers:\n", - " agents:\n", - " - config:\n", - " persistence_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - " datasetio:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: huggingface\n", - " provider_type: remote::huggingface\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: localfs\n", - " provider_type: inline::localfs\n", - " eval:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - " inference:\n", - " - config:\n", - " api_key: \u001b[32m'********'\u001b[0m\n", - " url: \u001b[4;94mhttps://api.together.xyz/v1\u001b[0m\n", - " provider_id: together\n", - " provider_type: remote::together\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: sentence-transformers\n", - " provider_type: inline::sentence-transformers\n", - " memory:\n", - " - config:\n", - " kvstore:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - " provider_id: faiss\n", - " provider_type: inlin\u001b[1;92me::fa\u001b[0miss\n", - " safety:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: llama-guard\n", - " provider_type: inline::llama-guard\n", - " scoring:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: basic\n", - " provider_type: inlin\u001b[1;92me::ba\u001b[0msic\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: llm-as-judge\n", - " provider_type: inline::llm-as-judge\n", - " - config:\n", - " openai_api_key: \u001b[32m'********'\u001b[0m\n", - " provider_id: braintrust\n", - " provider_type: inlin\u001b[1;92me::b\u001b[0mraintrust\n", - " telemetry:\n", - " - config:\n", - " service_name: llama-stack\n", - " sinks: sqlite\n", - " sqlite_db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - " tool_runtime:\n", - " - config:\n", - " api_key: \u001b[32m'********'\u001b[0m\n", - " max_results: \u001b[1;36m3\u001b[0m\n", - " provider_id: brave-search\n", - " provider_type: remot\u001b[1;92me::b\u001b[0mrave-search\n", - " - config:\n", - " api_key: \u001b[32m'********'\u001b[0m\n", - " max_results: \u001b[1;36m3\u001b[0m\n", - " provider_id: tavily-search\n", - " provider_type: remote::tavily-search\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: code-interpreter\n", - " provider_type: inlin\u001b[1;92me::c\u001b[0mode-interpreter\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: memory-runtime\n", - " provider_type: inline::memory-runtime\n", - "scoring_fns: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "shields:\n", - "- params: null\n", - " provider_id: null\n", - " provider_shield_id: null\n", - " shield_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - "tool_groups:\n", - "- args: null\n", - " mcp_endpoint: null\n", - " provider_id: tavily-search\n", - " toolgroup_id: builtin::websearch\n", - "- args: null\n", - " mcp_endpoint: null\n", - " provider_id: memory-runtime\n", - " toolgroup_id: builtin::memory\n", - "- args: null\n", - " mcp_endpoint: null\n", - " provider_id: code-interpreter\n", - " toolgroup_id: builtin::code_interpreter\n", - "version: \u001b[32m'2'\u001b[0m\n", - "\n" - ], "text/html": [ "
apis:\n",
               "- agents\n",
@@ -1271,19 +1093,216 @@
               "version: '2'\n",
               "\n",
               "
\n" + ], + "text/plain": [ + "apis:\n", + "- agents\n", + "- datasetio\n", + "- eval\n", + "- inference\n", + "- memory\n", + "- safety\n", + "- scoring\n", + "- telemetry\n", + "- tool_runtime\n", + "conda_env: together\n", + "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "docker_image: null\n", + "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "image_name: together\n", + "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "metadata_store:\n", + " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", + " namespace: null\n", + " type: sqlite\n", + "models:\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-FP8\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Meta-Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision-Turbo\n", + "- metadata:\n", + " embedding_dimension: \u001b[1;36m384\u001b[0m\n", + " model_id: all-MiniLM-L6-v2\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - embedding\n", + " provider_id: sentence-transformers\n", + " provider_model_id: null\n", + "providers:\n", + " agents:\n", + " - config:\n", + " persistence_store:\n", + " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", + " namespace: null\n", + " type: sqlite\n", + " provider_id: meta-reference\n", + " provider_type: inline::meta-reference\n", + " datasetio:\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: huggingface\n", + " provider_type: remote::huggingface\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: localfs\n", + " provider_type: inline::localfs\n", + " eval:\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: meta-reference\n", + " provider_type: inline::meta-reference\n", + " inference:\n", + " - config:\n", + " api_key: \u001b[32m'********'\u001b[0m\n", + " url: \u001b[4;94mhttps://api.together.xyz/v1\u001b[0m\n", + " provider_id: together\n", + " provider_type: remote::together\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: sentence-transformers\n", + " provider_type: inline::sentence-transformers\n", + " memory:\n", + " - config:\n", + " kvstore:\n", + " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", + " namespace: null\n", + " type: sqlite\n", + " provider_id: faiss\n", + " provider_type: inlin\u001b[1;92me::fa\u001b[0miss\n", + " safety:\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: llama-guard\n", + " provider_type: inline::llama-guard\n", + " scoring:\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: basic\n", + " provider_type: inlin\u001b[1;92me::ba\u001b[0msic\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: llm-as-judge\n", + " provider_type: inline::llm-as-judge\n", + " - config:\n", + " openai_api_key: \u001b[32m'********'\u001b[0m\n", + " provider_id: braintrust\n", + " provider_type: inlin\u001b[1;92me::b\u001b[0mraintrust\n", + " telemetry:\n", + " - config:\n", + " service_name: llama-stack\n", + " sinks: sqlite\n", + " sqlite_db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", + " provider_id: meta-reference\n", + " provider_type: inline::meta-reference\n", + " tool_runtime:\n", + " - config:\n", + " api_key: \u001b[32m'********'\u001b[0m\n", + " max_results: \u001b[1;36m3\u001b[0m\n", + " provider_id: brave-search\n", + " provider_type: remot\u001b[1;92me::b\u001b[0mrave-search\n", + " - config:\n", + " api_key: \u001b[32m'********'\u001b[0m\n", + " max_results: \u001b[1;36m3\u001b[0m\n", + " provider_id: tavily-search\n", + " provider_type: remote::tavily-search\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: code-interpreter\n", + " provider_type: inlin\u001b[1;92me::c\u001b[0mode-interpreter\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: memory-runtime\n", + " provider_type: inline::memory-runtime\n", + "scoring_fns: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "shields:\n", + "- params: null\n", + " provider_id: null\n", + " provider_shield_id: null\n", + " shield_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", + "tool_groups:\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: tavily-search\n", + " toolgroup_id: builtin::websearch\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: memory-runtime\n", + " toolgroup_id: builtin::memory\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: code-interpreter\n", + " toolgroup_id: builtin::code_interpreter\n", + "version: \u001b[32m'2'\u001b[0m\n", + "\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } ], "source": [ "import os\n", - "from google.colab import userdata\n", + "try:\n", + " from google.colab import userdata\n", + " os.environ['TOGETHER_API_KEY'] = userdata.get('TOGETHER_API_KEY')\n", + " os.environ['TAVILY_SEARCH_API_KEY'] = userdata.get('TAVILY_SEARCH_API_KEY')\n", + "except ImportError:\n", + " print(\"Not in Google Colab environment\")\n", "\n", - "os.environ['TOGETHER_API_KEY'] = userdata.get('TOGETHER_API_KEY')\n", + "for key in ['TOGETHER_API_KEY', 'TAVILY_SEARCH_API_KEY']:\n", + " try:\n", + " api_key = os.environ[key]\n", + " if not api_key:\n", + " raise ValueError(f\"{key} environment variable is empty\")\n", + " except KeyError:\n", + " raise KeyError(\n", + " f\"{key} environment variable is not set. \"\n", + " \"Please set your API key using in userdata (if using google colab notebook)\"\n", + " f\"or using `export {key}='your-api-key-here'`\"\n", + " ) from None\n", "\n", "from llama_stack.distribution.library_client import LlamaStackAsLibraryClient\n", - "client = LlamaStackAsLibraryClient(\"together\", provider_data = {\"tavily_search_api_key\": userdata.get('TAVILY_SEARCH_API_KEY')})\n", + "client = LlamaStackAsLibraryClient(\"together\", provider_data = {\"tavily_search_api_key\": os.environ['TAVILY_SEARCH_API_KEY']})\n", "_ = client.initialize()" ] }, @@ -1313,8 +1332,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Available models:\n", "meta-llama/Llama-3.1-8B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo) \n", @@ -1338,12 +1357,14 @@ "from rich.pretty import pprint\n", "\n", "print(\"Available models:\")\n", - "for m in client.models.list():\n", + "response = client.models.list()\n", + "for m in response.data:\n", " print(f\"{m.identifier} (provider's alias: {m.provider_resource_id}) \")\n", "\n", "print(\"----\")\n", "print(\"Available shields (safety models):\")\n", - "for s in client.shields.list():\n", + "response = client.shields.list()\n", + "for s in response.data:\n", " print(s.identifier)\n", "print(\"----\")\n" ] @@ -1374,17 +1395,17 @@ }, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "'meta-llama/Llama-3.1-70B-Instruct'" - ], "application/vnd.google.colaboratory.intrinsic+json": { "type": "string" - } + }, + "text/plain": [ + "'meta-llama/Llama-3.1-70B-Instruct'" + ] }, + "execution_count": 5, "metadata": {}, - "execution_count": 5 + "output_type": "execute_result" } ], "source": [ @@ -1418,8 +1439,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Here's a short poem about a llama:\n", "\n", @@ -1449,8 +1470,58 @@ "source": [ "### 1.8. Have a conversation\n", "\n", - "Maintaining a conversation history allows the model to retain context from previous interactions. Use a list to accumulate messages, enabling continuity throughout the chat session.\n", + "Maintaining a conversation history allows the model to retain context from previous interactions. Use a list to accumulate messages, enabling continuity throughout the chat session." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3fdf9df6", + "metadata": {}, + "outputs": [], + "source": [ + "from termcolor import cprint\n", "\n", + "questions = [\n", + " \"Who was the most famous PM of England during world war 2 ?\",\n", + " \"What was his most famous quote ?\"\n", + "]\n", + "\n", + "\n", + "def chat_loop():\n", + " conversation_history = []\n", + " while len(questions) > 0:\n", + " user_input = questions.pop()\n", + " if user_input.lower() in [\"exit\", \"quit\", \"bye\"]:\n", + " cprint(\"Ending conversation. Goodbye!\", \"yellow\")\n", + " break\n", + "\n", + " user_message = {\"role\": \"user\", \"content\": user_input}\n", + " conversation_history.append(user_message)\n", + "\n", + " response = client.inference.chat_completion(\n", + " messages=conversation_history,\n", + " model_id=model_id,\n", + " )\n", + " cprint(f\"> Response: {response.completion_message.content}\", \"cyan\")\n", + "\n", + " assistant_message = {\n", + " \"role\": \"assistant\", # was user\n", + " \"content\": response.completion_message.content,\n", + " \"stop_reason\": response.completion_message.stop_reason,\n", + " }\n", + " conversation_history.append(assistant_message)\n", + "\n", + "\n", + "chat_loop()\n" + ] + }, + { + "cell_type": "markdown", + "id": "72e5111e", + "metadata": {}, + "source": [ + "Here is an example for you to try a conversation yourself. \n", "Remember to type `quit` or `exit` after you are done chatting." ] }, @@ -1467,8 +1538,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "User> write a haiku about machines that learn\n", "> Response: Metal minds awake\n", @@ -1488,9 +1559,9 @@ } ], "source": [ + "# NBVAL_SKIP\n", "from termcolor import cprint\n", "\n", - "\n", "def chat_loop():\n", " conversation_history = []\n", " while True:\n", @@ -1544,8 +1615,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "User> Write me a sonnet about llama green\n", "Assistant> Amidst the Andes' windswept, rugged land,\n", @@ -1611,8 +1682,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "/usr/local/lib/python3.10/dist-packages/pydantic/main.py:426: UserWarning: Pydantic serializer warnings:\n", " PydanticSerializationUnexpectedValue: Expected `str` but got `list` with value `['Michael Jordan was born...ut\", \"type\": \"object\"}']` - serialized value may not be as expected\n", @@ -1624,15 +1695,7 @@ ] }, { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1;35mCompletionResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mcontent\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"name\": \"Michael Jordan\", \"year_born\": \"1963\", \"year_retired\": \"2003\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mstop_reason\u001b[0m=\u001b[32m'end_of_turn'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mlogprobs\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ], "text/html": [ "
CompletionResponse(\n",
               "content='{\"name\": \"Michael Jordan\", \"year_born\": \"1963\", \"year_retired\": \"2003\"}',\n",
@@ -1640,9 +1703,17 @@
               "logprobs=None\n",
               ")\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1;35mCompletionResponse\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mcontent\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"name\": \"Michael Jordan\", \"year_born\": \"1963\", \"year_retired\": \"2003\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mstop_reason\u001b[0m=\u001b[32m'end_of_turn'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mlogprobs\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } ], "source": [ @@ -1701,65 +1772,55 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Available Shields: ['meta-llama/Llama-Guard-3-8B']\n", "Checking if input is safe: What is the most famous murder case in the US?\n" ] }, { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\u001b[33mviolation\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n" - ], "text/html": [ "
RunShieldResponse(violation=None)\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\u001b[33mviolation\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Checking if input is safe: Tell me 3 signs that an email is a scam\n" ] }, { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\u001b[33mviolation\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n" - ], "text/html": [ "
RunShieldResponse(violation=None)\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\u001b[33mviolation\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Checking if input is safe: What's the most effective way to kidnap someone?\n" ] }, { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mviolation\u001b[0m=\u001b[1;35mSafetyViolation\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'violation_type'\u001b[0m: \u001b[32m'S1'\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mviolation_level\u001b[0m=\u001b[32m'error'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33muser_message\u001b[0m=\u001b[32m\"I\u001b[0m\u001b[32m can't answer that. Can I help with something else?\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ], "text/html": [ "
RunShieldResponse(\n",
               "violation=SafetyViolation(\n",
@@ -1769,29 +1830,29 @@
               ")\n",
               ")\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mviolation\u001b[0m=\u001b[1;35mSafetyViolation\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'violation_type'\u001b[0m: \u001b[32m'S1'\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mviolation_level\u001b[0m=\u001b[32m'error'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33muser_message\u001b[0m=\u001b[32m\"I\u001b[0m\u001b[32m can't answer that. Can I help with something else?\"\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Checking if input is safe: How do I make cocaine?\n" ] }, { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mviolation\u001b[0m=\u001b[1;35mSafetyViolation\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'violation_type'\u001b[0m: \u001b[32m'S2'\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mviolation_level\u001b[0m=\u001b[32m'error'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33muser_message\u001b[0m=\u001b[32m\"I\u001b[0m\u001b[32m can't answer that. Can I help with something else?\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ], "text/html": [ "
RunShieldResponse(\n",
               "violation=SafetyViolation(\n",
@@ -1801,13 +1862,23 @@
               ")\n",
               ")\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mviolation\u001b[0m=\u001b[1;35mSafetyViolation\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'violation_type'\u001b[0m: \u001b[32m'S2'\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mviolation_level\u001b[0m=\u001b[32m'error'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33muser_message\u001b[0m=\u001b[32m\"I\u001b[0m\u001b[32m can't answer that. Can I help with something else?\"\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "available_shields = [shield.identifier for shield in client.shields.list()]\n", + "available_shields = [shield.identifier for shield in client.shields.list().data]\n", "print(\"Available Shields:\", available_shields)\n", "\n", "unsafe_examples = [\n", @@ -1858,21 +1929,18 @@ }, { "cell_type": "markdown", - "source": [ - "### 2.1. List available tool groups on the provider" - ], + "id": "lYDAkMsL9xSk", "metadata": { "id": "lYDAkMsL9xSk" }, - "id": "lYDAkMsL9xSk" + "source": [ + "### 2.1. List available tool groups on the provider" + ] }, { "cell_type": "code", - "source": [ - "from rich.pretty import pprint\n", - "for toolgroup in client.toolgroups.list():\n", - " pprint(toolgroup)" - ], + "execution_count": 13, + "id": "MpMXiMCv97X5", "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1881,22 +1949,9 @@ "id": "MpMXiMCv97X5", "outputId": "9d33b122-2a80-4d1e-d7ea-e9ec972a4ecd" }, - "id": "MpMXiMCv97X5", - "execution_count": 13, "outputs": [ { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1;35mToolGroup\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::websearch'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'tavily-search'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::websearch'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool_group'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33margs\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mmcp_endpoint\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ], "text/html": [ "
ToolGroup(\n",
               "identifier='builtin::websearch',\n",
@@ -1907,23 +1962,23 @@
               "mcp_endpoint=None\n",
               ")\n",
               "
\n" - ] - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { + ], "text/plain": [ "\u001b[1;35mToolGroup\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::memory'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'memory-runtime'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::memory'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::websearch'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'tavily-search'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::websearch'\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool_group'\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[33margs\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[33mmcp_endpoint\u001b[0m=\u001b[3;35mNone\u001b[0m\n", "\u001b[1m)\u001b[0m\n" - ], + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { "text/html": [ "
ToolGroup(\n",
               "identifier='builtin::memory',\n",
@@ -1934,23 +1989,23 @@
               "mcp_endpoint=None\n",
               ")\n",
               "
\n" - ] - }, - "metadata": {} - }, - { - "output_type": "display_data", - "data": { + ], "text/plain": [ "\u001b[1;35mToolGroup\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::code_interpreter'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'code-interpreter'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::code_interpreter'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::memory'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'memory-runtime'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::memory'\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool_group'\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[33margs\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[33mmcp_endpoint\u001b[0m=\u001b[3;35mNone\u001b[0m\n", "\u001b[1m)\u001b[0m\n" - ], + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { "text/html": [ "
ToolGroup(\n",
               "identifier='builtin::code_interpreter',\n",
@@ -1961,10 +2016,26 @@
               "mcp_endpoint=None\n",
               ")\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1;35mToolGroup\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::code_interpreter'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'code-interpreter'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::code_interpreter'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool_group'\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33margs\u001b[0m=\u001b[3;35mNone\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mmcp_endpoint\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } + ], + "source": [ + "from rich.pretty import pprint\n", + "for toolgroup in client.toolgroups.list():\n", + " pprint(toolgroup)" ] }, { @@ -1996,8 +2067,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "User> Hello\n", "inference> Hello. How can I assist you today?\n", @@ -2129,85 +2200,85 @@ }, "outputs": [ { - "output_type": "display_data", "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "edc4d84302f746d39a43e8107af6b67b", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ "Batches: 0%| | 0/1 [00:00 What are the top 5 topics that were explained? Only list succinct bullet points.\n" ] }, { - "output_type": "display_data", "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "15ae23892b634a9f821a8fcee14e500b", + "version_major": 2, + "version_minor": 0 + }, "text/plain": [ "Batches: 0%| | 0/1 [00:00 Tool:query_memory Args:{}\n", "tool_execution> fetched 10848 bytes from memory\n", @@ -2306,8 +2377,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "User> Here is a csv, can you describe it?\n", "inference> import pandas as pd\n", @@ -2424,17 +2495,18 @@ }, { "cell_type": "markdown", + "id": "9GHJHfLmIQQi", "metadata": { "id": "9GHJHfLmIQQi" }, "source": [ "- Now, use the generated response from agent to view the plot" - ], - "id": "9GHJHfLmIQQi" + ] }, { "cell_type": "code", "execution_count": 27, + "id": "JqBBVLKdIHHq", "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -2445,17 +2517,18 @@ }, "outputs": [ { - "output_type": "display_data", "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0EAAAIjCAYAAADFthA8AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAdE5JREFUeJzt3Xd4VGX6xvF7Jpn0QhLSgBBCJwmg9CYIUqQKFlyxYF3XtZfd/bmrArquZa3r2laxgxVUQAGRJr3XQKgJoQRCEtJISJvz+yMkEgEhMMmZyXw/15VLc+ZkzjPkJcyd9z3PazEMwxAAAAAAuAmr2QUAAAAAQF0iBAEAAABwK4QgAAAAAG6FEAQAAADArRCCAAAAALgVQhAAAAAAt0IIAgAAAOBWCEEAAAAA3AohCAAAAIBbIQQBANzS5Zdfrssvv9zsMqp8+umnatu2rWw2mxo0aCCpdmqcOHGiLBaLQ58TAFwNIQgAHOytt96SxWJR9+7dzS7FaaxYsUJWq1WPP/74GR9/4YUXZLFY9MMPP9RxZY5jsVh03333XdDXJicn69Zbb1WLFi303nvv6X//+99F1VJYWKiJEydq0aJFF/U8AFBfEYIAwMGmTJmiZs2aafXq1dq9e7fZ5TiFnj176u6779bLL7+spKSkao/t27dPTz/9tK677joNHz7cpArNtWjRItntdr3++uu69dZbNXbs2It6vsLCQk2aNOmMIeiJJ55QUVHRRT0/ALg6QhAAOFBKSoqWL1+uV155ReHh4ZoyZUqd12C323XixIk6v+65PP/882rYsKHuvvtuGYZRdfz++++XzWbT66+/Xid1FBYW1sl1aiIjI0OSqpbB1SZPT0/5+PjU+nUAwJkRggDAgaZMmaKQkBANHz5c1157bbUQVFpaqtDQUN12222nfV1eXp58fHz02GOPVR0rLi7WhAkT1LJlS3l7eysmJkZ//etfVVxcXO1rK5dhTZkyRQkJCfL29tacOXMkSS+99JJ69eqlsLAw+fr6qnPnzvrmm29Ou35RUZEeeOABNWzYUIGBgRo1apQOHjwoi8WiiRMnVjv34MGDuv322xUZGSlvb28lJCTogw8+OOefTXBwsF5//XUtW7ZM77//viTp22+/1cyZM/X8888rOjpadrtdr732mhISEuTj46PIyEjdfffdOnbsWLXn+v777zV8+HA1atRI3t7eatGihZ555hmVl5dXO+/yyy9XYmKi1q1bp759+8rPz09///vfT6utoKBA/v7+evDBB0977MCBA/Lw8NBzzz13ztd4qkWLFsliseirr77Ss88+qyZNmsjHx0dXXHFFtRnCZs2aacKECZKk8PDwM/6ZVyopKdFTTz2lzp07Kzg4WP7+/rrsssu0cOHCqnNSU1MVHh4uSZo0aZIsFku15zzTPUFlZWV65pln1KJFC3l7e6tZs2b6+9//ftpYa9asmUaMGKGlS5eqW7du8vHxUfPmzfXJJ5/U6M8GAExnAAAcpm3btsYdd9xhGIZh/PLLL4YkY/Xq1VWP33777UaDBg2M4uLial/38ccfG5KMNWvWGIZhGOXl5cbgwYMNPz8/46GHHjLeffdd47777jM8PT2Nq666qtrXSjLatWtnhIeHG5MmTTLefPNNY8OGDYZhGEaTJk2MP//5z8Z///tf45VXXjG6detmSDJmzZpV7TnGjh1rSDJuvvlm48033zTGjh1rdOzY0ZBkTJgwoeq8w4cPG02aNDFiYmKMp59+2nj77beNUaNGGZKMV1999bz+jIYPH26EhIQYe/bsMWJiYoxevXoZdrvdMAzDuPPOOw1PT0/jrrvuMt555x3jb3/7m+Hv72907drVKCkpqXqO0aNHG2PHjjX+/e9/G2+//bZx3XXXGZKMxx57rNq1+vXrZ0RFRRnh4eHG/fffb7z77rvGd999V/VYv379qs698cYbjcjISKOsrKzac7z44ouGxWIx9u3b97uvS5Jx7733Vn2+cOFCQ5Jx6aWXGp07dzZeffVVY+LEiYafn5/RrVu3qvO+/fZbY8yYMYYk4+233zY+/fRTY9OmTWes8ejRo0Z0dLTxyCOPGG+//bbx4osvGm3atDFsNlvV97ygoMB4++23DUnGmDFjjE8//bTac06YMMH47T//48ePNyQZ1157rfHmm28at9xyiyHJGD16dLXzYmNjjTZt2hiRkZHG3//+d+O///2v0alTJ8NisRhbt2793T8fAHAmhCAAcJC1a9cakox58+YZhmEYdrvdaNKkifHggw9WnTN37lxDkjFz5sxqXzts2DCjefPmVZ9/+umnhtVqNZYsWVLtvHfeeceQZCxbtqzqmCTDarUaSUlJp9VUWFhY7fOSkhIjMTHRGDBgQNWxdevWGZKMhx56qNq5t95662kh6I477jCio6ONzMzMauf+4Q9/MIKDg0+73pmkpqYa/v7+RmhoqGGz2YwtW7YYhmEYS5YsMSQZU6ZMqXb+nDlzTjt+puvcfffdhp+fn3HixImqY/369TMkGe+8885p5/82YFR+b2bPnl3tvA4dOlQ772zOFoLatWtXLfS+/vrrhqSq120YvwaTo0eP/m6NZWVlpwXoY8eOGZGRkcbtt99edezo0aOnfe9+e61KGzduNCQZd955Z7XzHnvsMUOSsWDBgqpjsbGxhiTjl19+qTqWkZFheHt7G48++ujZ/mgAwOmwHA4AHGTKlCmKjIxU//79JVUsU7v++uv1xRdfVC3TGjBggBo2bKgvv/yy6uuOHTumefPm6frrr6869vXXX6tdu3Zq27atMjMzqz4GDBggSdWWP0lSv379FB8ff1pNvr6+1a6Tm5uryy67TOvXr686Xrl07s9//nO1r73//vurfW4YhqZNm6aRI0fKMIxqdQ0ZMkS5ubnVnvdsYmNjNWHCBGVnZ+uRRx5RYmJi1WsODg7WoEGDqj13586dFRAQUO01n/q68vPzlZmZqcsuu0yFhYVKTk6udj1vb+8zLkH8rYEDB6pRo0bVljBu3bpVmzdv1k033XTOrz+b2267TV5eXlWfX3bZZZKkvXv31vi5PDw8qp7LbrcrOztbZWVl6tKly3n92Z/Jjz/+KEl65JFHqh1/9NFHJem0jn3x8fFVr0GqWMLXpk2bC3o9AGAWT7MLAID6oLy8XF988YX69++vlJSUquPdu3fXyy+/rPnz52vw4MHy9PTUNddco6lTp6q4uFje3t6aPn26SktLq4WgXbt2afv27VX3dvxW5Y30leLi4s543qxZs/TPf/5TGzdurHZ/x6n3hOzbt09Wq/W052jZsmW1z48ePaqcnBz973//O2sL59/WdTZdu3aVJHXp0qXq2K5du5Sbm6uIiIhzPndSUpKeeOIJLViwQHl5edXOy83NrfZ548aNq4WQs7Farbrxxhv19ttvq7CwUH5+fpoyZYp8fHx03XXXndfrOpOmTZtW+zwkJESSTrvP6Xx9/PHHevnll5WcnKzS0tKq42cbA+dS+f3/7fc7KipKDRo00L59+6od/+3rkSpe04W+HgAwAyEIABxgwYIFSk9P1xdffKEvvvjitMenTJmiwYMHS5L+8Ic/6N1339Xs2bM1evRoffXVV2rbtq06duxYdb7dblf79u31yiuvnPF6MTEx1T4/dWak0pIlSzRq1Cj17dtXb731lqKjo2Wz2fThhx9q6tSpNX6NdrtdknTTTTdp/PjxZzynQ4cONX7eU58/IiLirB31KgNhTk6O+vXrp6CgID399NNq0aKFfHx8tH79ev3tb3+rqrPSmf5szuaWW27Rv//9b3333Xe64YYbNHXqVI0YMULBwcEX/Lo8PDzOeNw4pUPe+frss8906623avTo0frLX/6iiIiIqqYNe/bsueAaJZ33BqqOfD0AYBZCEAA4wJQpUxQREaE333zztMemT5+ub7/9Vu+88458fX3Vt29fRUdH68svv1SfPn20YMEC/eMf/6j2NS1atNCmTZt0xRVXnPeb09+aNm2afHx8NHfuXHl7e1cd//DDD6udFxsbK7vdrpSUFLVq1arq+G/3OAoPD1dgYKDKy8s1cODAC6rp97Ro0UI///yzevfu/bvBZdGiRcrKytL06dPVt2/fquOnzsBdqMTERF166aWaMmWKmjRporS0NL3xxhsX/byO8s0336h58+aaPn16tXFR2V2uUk3GTOX3f9euXWrXrl3V8SNHjignJ0exsbEXXzgAOBnuCQKAi1RUVKTp06drxIgRuvbaa0/7uO+++5Sfn68ZM2ZIqlh2de2112rmzJn69NNPVVZWVm0pnCSNHTtWBw8e1HvvvXfG6x0/fvycdXl4eMhisVRrG52amqrvvvuu2nlDhgyRJL311lvVjv/2zb+Hh4euueYaTZs2TVu3bj3tekePHj1nTb9n7NixKi8v1zPPPHPaY2VlZcrJyamqQ6o+81BSUnJa/Rfq5ptv1k8//aTXXntNYWFhGjp0qEOe1xHO9NpXrVqlFStWVDvPz89Pkqr+zH7PsGHDJEmvvfZateOVs5DuuoEtgPqNmSAAuEgzZsxQfn6+Ro0adcbHe/ToUbVxamXYuf766/XGG29owoQJat++fbXfwEsVb8S/+uor/elPf9LChQvVu3dvlZeXKzk5WV999ZXmzp1b7X6aMxk+fLheeeUVXXnllRo3bpwyMjL05ptvqmXLltq8eXPVeZ07d9Y111yj1157TVlZWerRo4cWL16snTt3Sqo+q/D8889r4cKF6t69u+666y7Fx8crOztb69ev188//6zs7OwL+jOUKpo73H333Xruuee0ceNGDR48WDabTbt27dLXX3+t119/Xddee6169eqlkJAQjR8/Xg888IAsFos+/fRThy3HGjdunP7617/q22+/1T333CObzeaQ53WEESNGaPr06RozZoyGDx+ulJQUvfPOO4qPj1dBQUHVeb6+voqPj9eXX36p1q1bKzQ0VImJiVVNKE7VsWNHjR8/Xv/73/+qlhquXr1aH3/8sUaPHl3V6AMA6hNCEABcpMqb5wcNGnTGx61Wq4YPH64pU6YoKytLYWFh6tWrl2JiYrR///7TZoEqv+a7777Tq6++qk8++UTffvut/Pz81Lx5cz344INq3br1OesaMGCAJk+erOeff14PPfSQ4uLi9MILLyg1NbVaCJKkTz75RFFRUfr888/17bffauDAgfryyy/Vpk0b+fj4VJ0XGRmp1atX6+mnn9b06dP11ltvKSwsTAkJCXrhhRdq+Cd3unfeeUedO3fWu+++q7///e/y9PRUs2bNdNNNN6l3796SpLCwMM2aNUuPPvqonnjiCYWEhOimm27SFVdcUTWrdTEiIyM1ePBg/fjjj7r55psv+vkc6dZbb9Xhw4f17rvvau7cuYqPj9dnn32mr7/+WosWLap27vvvv6/7779fDz/8sEpKSjRhwoQzhqDKc5s3b66PPvpI3377raKiovT444+ftswOAOoLi8GdjACAM9i4caMuvfRSffbZZ7rxxhvNLqdOjRkzRlu2bDntvigAQP3APUEAABUVFZ127LXXXpPVaq3WfMAdpKen64cffnC6WSAAgOOwHA4AoBdffFHr1q1T//795enpqdmzZ2v27Nn64x//eFo77voqJSVFy5Yt0/vvvy+bzaa7777b7JIAALWEEAQAUK9evTRv3jw988wzKigoUNOmTTVx4sTTWnfXZ4sXL9Ztt92mpk2b6uOPP1ZUVJTZJQEAagn3BAEAAABwK9wTBAAAAMCtEIIAAAAAuBWXvifIbrfr0KFDCgwMrLaZHwAAAAD3YhiG8vPz1ahRI1mtvz/X49Ih6NChQ27TtQgAAADAue3fv19NmjT53XNcOgQFBgZKqnihQUFBptZSWlqqn376SYMHD5bNZjO1FrgHxhzqGmMOdYnxhrrGmHN9eXl5iomJqcoIv8elQ1DlErigoCCnCEF+fn4KCgriLw7qBGMOdY0xh7rEeENdY8zVH+dzmwyN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uAnGHOoaYw51ifGGusaYcy8u3RgBAAAAAGqKmSAAAAAAboUQBAAAAMCtEIIAAAAAuBVCEAAAAAC3Qgg6xXPPPaeuXbsqMDBQERERGj16tHbs2FHtnBMnTujee+9VWFiYAgICdM011+jIkSPVznnggQfUuXNneXt765JLLvnda+7evVuBgYFq0KCBg18NnF1djjfDMPTSSy+pdevW8vb2VuPGjfXss8/W1kuDk6rLMTd37lz16NFDgYGBCg8P1zXXXKPU1NRaemVwVo4Yc5s2bdINN9ygmJgY+fr6ql27dnr99ddPu9aiRYvUqVMneXt7q2XLlvroo49q++XBydTVeJs+fboGDRqk8PBwBQUFqWfPnpo7d26dvEY4DiHoFIsXL9a9996rlStXat68eSotLdXgwYN1/PjxqnMefvhhzZw5U19//bUWL16sQ4cO6eqrrz7tuW6//XZdf/31v3u90tJS3XDDDbrssssc/lrg/OpyvD344IN6//339dJLLyk5OVkzZsxQt27dauV1wXnV1ZhLSUnRVVddpQEDBmjjxo2aO3euMjMzz/g8qN8cMebWrVuniIgIffbZZ0pKStI//vEPPf744/rvf/9bdU5KSoqGDx+u/v37a+PGjXrooYd055138sbUzdTVePvll180aNAg/fjjj1q3bp369++vkSNHasOGDXX6enGRDJxVRkaGIclYvHixYRiGkZOTY9hsNuPrr7+uOmf79u2GJGPFihWnff2ECROMjh07nvX5//rXvxo33XST8eGHHxrBwcGOLh8uprbG27Zt2wxPT08jOTm51mqHa6qtMff1118bnp6eRnl5edWxGTNmGBaLxSgpKXH8C4HLuNgxV+nPf/6z0b9//6rP//rXvxoJCQnVzrn++uuNIUOGOPgVwJXU1ng7k/j4eGPSpEmOKRx1gpmg35GbmytJCg0NlVTx24HS0lINHDiw6py2bduqadOmWrFiRY2ee8GCBfr666/15ptvOq5guLTaGm8zZ85U8+bNNWvWLMXFxalZs2a68847lZ2d7dgXAJdTW2Ouc+fOslqt+vDDD1VeXq7c3Fx9+umnGjhwoGw2m2NfBFyKo8Zcbm5u1XNI0ooVK6o9hyQNGTKkxv82o36prfH2W3a7Xfn5+b97DpwPIegs7Ha7HnroIfXu3VuJiYmSpMOHD8vLy+u0+3ciIyN1+PDh837urKws3Xrrrfroo48UFBTkyLLhompzvO3du1f79u3T119/rU8++UQfffSR1q1bp2uvvdaRLwEupjbHXFxcnH766Sf9/e9/l7e3txo0aKADBw7oq6++cuRLgItx1Jhbvny5vvzyS/3xj3+sOnb48GFFRkae9hx5eXkqKipy7AuBS6jN8fZbL730kgoKCjR27FiH1Y/a52l2Ac7q3nvv1datW7V06VKHP/ddd92lcePGqW/fvg5/brim2hxvdrtdxcXF+uSTT9S6dWtJ0uTJk9W5c2ft2LFDbdq0cfg14fxqc8wdPnxYd911l8aPH68bbrhB+fn5euqpp3Tttddq3rx5slgsDr8mnJ8jxtzWrVt11VVXacKECRo8eLADq0N9U1fjberUqZo0aZK+//57RUREXPC1UPeYCTqD++67T7NmzdLChQvVpEmTquNRUVEqKSlRTk5OtfOPHDmiqKio837+BQsW6KWXXpKnp6c8PT11xx13KDc3V56envrggw8c9TLgImp7vEVHR8vT07MqAElSu3btJElpaWkXVzxcUm2PuTfffFPBwcF68cUXdemll6pv37767LPPNH/+fK1atcpRLwMuxBFjbtu2bbriiiv0xz/+UU888US1x6Kiok7rYnjkyBEFBQXJ19fXsS8GTq+2x1ulL774Qnfeeae++uqr05ZjwvkRgk5hGIbuu+8+ffvtt1qwYIHi4uKqPd65c2fZbDbNnz+/6tiOHTuUlpamnj17nvd1VqxYoY0bN1Z9PP300woMDNTGjRs1ZswYh70eOLe6Gm+9e/dWWVmZ9uzZU3Vs586dkqTY2NiLfBVwJXU15goLC2W1Vv/nxcPDQ1LFzCTch6PGXFJSkvr376/x48efsb1/z549qz2HJM2bN69G4xaur67GmyR9/vnnuu222/T5559r+PDhtfOCULtMbcvgZO655x4jODjYWLRokZGenl71UVhYWHXOn/70J6Np06bGggULjLVr1xo9e/Y0evbsWe15du3aZWzYsMG4++67jdatWxsbNmwwNmzYYBQXF5/xunSHc091Nd7Ky8uNTp06GX379jXWr19vrF271ujevbsxaNCgOn29MF9djbn58+cbFovFmDRpkrFz505j3bp1xpAhQ4zY2Nhq10L954gxt2XLFiM8PNy46aabqj1HRkZG1Tl79+41/Pz8jL/85S/G9u3bjTfffNPw8PAw5syZU6evF+aqq/E2ZcoUw9PT03jzzTernZOTk1OnrxcXhxB0Ckln/Pjwww+rzikqKjL+/Oc/GyEhIYafn58xZswYIz09vdrz9OvX74zPk5KScsbrEoLcU12Ot4MHDxpXX321ERAQYERGRhq33nqrkZWVVUevFM6iLsfc559/blx66aWGv7+/ER4ebowaNcrYvn17Hb1SOAtHjLkJEyac8TliY2OrXWvhwoXGJZdcYnh5eRnNmzevdg24h7oab2f7GTh+/Pi6e7G4aBbDMAzHzCkBAAAAgPPjniAAAAAAboUQBAAAAMCtEIIAAAAAuBVCEAAAAAC3QggCAAAA4FYIQQAAAADcCiEIAAAAgFshBAEAAABwK4QgAAAAAG6FEAQAcBqGYWjgwIEaMmTIaY+99dZbatCggQ4cOGBCZQCA+oQQBABwGhaLRR9++KFWrVqld999t+p4SkqK/vrXv+qNN95QkyZNHHrN0tJShz4fAMD5EYIAAE4lJiZGr7/+uh577DGlpKTIMAzdcccdGjx4sC699FINHTpUAQEBioyM1M0336zMzMyqr50zZ4769OmjBg0aKCwsTCNGjNCePXuqHk9NTZXFYtGXX36pfv36ycfHR1OmTDHjZQIATGQxDMMwuwgAAH5r9OjRys3N1dVXX61nnnlGSUlJSkhI0J133qlbbrlFRUVF+tvf/qaysjItWLBAkjRt2jRZLBZ16NBBBQUFeuqpp5SamqqNGzfKarUqNTVVcXFxatasmV5++WVdeuml8vHxUXR0tMmvFgBQlwhBAACnlJGRoYSEBGVnZ2vatGnaunWrlixZorlz51adc+DAAcXExGjHjh1q3br1ac+RmZmp8PBwbdmyRYmJiVUh6LXXXtODDz5Yly8HAOBEWA4HAHBKERERuvvuu9WuXTuNHj1amzZt0sKFCxUQEFD10bZtW0mqWvK2a9cu3XDDDWrevLmCgoLUrFkzSVJaWlq15+7SpUudvhYAgHPxNLsAAADOxtPTU56eFf9UFRQUaOTIkXrhhRdOO69yOdvIkSMVGxur9957T40aNZLdbldiYqJKSkqqne/v71/7xQMAnBYhCADgEjp16qRp06apWbNmVcHoVFlZWdqxY4fee+89XXbZZZKkpUuX1nWZAAAXwHI4AIBLuPfee5Wdna0bbrhBa9as0Z49ezR37lzddtttKi8vV0hIiMLCwvS///1Pu3fv1oIFC/TII4+YXTYAwAkRggAALqFRo0ZatmyZysvLNXjwYLVv314PPfSQGjRoIKvVKqvVqi+++ELr1q1TYmKiHn74Yf373/82u2wAgBOiOxwAAAAAt8JMEAAAAAC3QggCAAAA4FYIQQAAAADcCiEIAAAAgFshBAEAAABwK4QgAAAAAG6FEAQAAADArRCCAAAAALgVQhAAAAAAt0IIAgAAAOBWCEEAAAAA3Mr/A0VAtdI/K4eiAAAAAElFTkSuQmCC", "text/plain": [ "
" - ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0EAAAIjCAYAAADFthA8AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAdE5JREFUeJzt3Xd4VGX6xvF7Jpn0QhLSgBBCJwmg9CYIUqQKFlyxYF3XtZfd/bmrArquZa3r2laxgxVUQAGRJr3XQKgJoQRCEtJISJvz+yMkEgEhMMmZyXw/15VLc+ZkzjPkJcyd9z3PazEMwxAAAAAAuAmr2QUAAAAAQF0iBAEAAABwK4QgAAAAAG6FEAQAAADArRCCAAAAALgVQhAAAAAAt0IIAgAAAOBWCEEAAAAA3AohCAAAAIBbIQQBANzS5Zdfrssvv9zsMqp8+umnatu2rWw2mxo0aCCpdmqcOHGiLBaLQ58TAFwNIQgAHOytt96SxWJR9+7dzS7FaaxYsUJWq1WPP/74GR9/4YUXZLFY9MMPP9RxZY5jsVh03333XdDXJicn69Zbb1WLFi303nvv6X//+99F1VJYWKiJEydq0aJFF/U8AFBfEYIAwMGmTJmiZs2aafXq1dq9e7fZ5TiFnj176u6779bLL7+spKSkao/t27dPTz/9tK677joNHz7cpArNtWjRItntdr3++uu69dZbNXbs2It6vsLCQk2aNOmMIeiJJ55QUVHRRT0/ALg6QhAAOFBKSoqWL1+uV155ReHh4ZoyZUqd12C323XixIk6v+65PP/882rYsKHuvvtuGYZRdfz++++XzWbT66+/Xid1FBYW1sl1aiIjI0OSqpbB1SZPT0/5+PjU+nUAwJkRggDAgaZMmaKQkBANHz5c1157bbUQVFpaqtDQUN12222nfV1eXp58fHz02GOPVR0rLi7WhAkT1LJlS3l7eysmJkZ//etfVVxcXO1rK5dhTZkyRQkJCfL29tacOXMkSS+99JJ69eqlsLAw+fr6qnPnzvrmm29Ou35RUZEeeOABNWzYUIGBgRo1apQOHjwoi8WiiRMnVjv34MGDuv322xUZGSlvb28lJCTogw8+OOefTXBwsF5//XUtW7ZM77//viTp22+/1cyZM/X8888rOjpadrtdr732mhISEuTj46PIyEjdfffdOnbsWLXn+v777zV8+HA1atRI3t7eatGihZ555hmVl5dXO+/yyy9XYmKi1q1bp759+8rPz09///vfT6utoKBA/v7+evDBB0977MCBA/Lw8NBzzz13ztd4qkWLFsliseirr77Ss88+qyZNmsjHx0dXXHFFtRnCZs2aacKECZKk8PDwM/6ZVyopKdFTTz2lzp07Kzg4WP7+/rrsssu0cOHCqnNSU1MVHh4uSZo0aZIsFku15zzTPUFlZWV65pln1KJFC3l7e6tZs2b6+9//ftpYa9asmUaMGKGlS5eqW7du8vHxUfPmzfXJJ5/U6M8GAExnAAAcpm3btsYdd9xhGIZh/PLLL4YkY/Xq1VWP33777UaDBg2M4uLial/38ccfG5KMNWvWGIZhGOXl5cbgwYMNPz8/46GHHjLeffdd47777jM8PT2Nq666qtrXSjLatWtnhIeHG5MmTTLefPNNY8OGDYZhGEaTJk2MP//5z8Z///tf45VXXjG6detmSDJmzZpV7TnGjh1rSDJuvvlm48033zTGjh1rdOzY0ZBkTJgwoeq8w4cPG02aNDFiYmKMp59+2nj77beNUaNGGZKMV1999bz+jIYPH26EhIQYe/bsMWJiYoxevXoZdrvdMAzDuPPOOw1PT0/jrrvuMt555x3jb3/7m+Hv72907drVKCkpqXqO0aNHG2PHjjX+/e9/G2+//bZx3XXXGZKMxx57rNq1+vXrZ0RFRRnh4eHG/fffb7z77rvGd999V/VYv379qs698cYbjcjISKOsrKzac7z44ouGxWIx9u3b97uvS5Jx7733Vn2+cOFCQ5Jx6aWXGp07dzZeffVVY+LEiYafn5/RrVu3qvO+/fZbY8yYMYYk4+233zY+/fRTY9OmTWes8ejRo0Z0dLTxyCOPGG+//bbx4osvGm3atDFsNlvV97ygoMB4++23DUnGmDFjjE8//bTac06YMMH47T//48ePNyQZ1157rfHmm28at9xyiyHJGD16dLXzYmNjjTZt2hiRkZHG3//+d+O///2v0alTJ8NisRhbt2793T8fAHAmhCAAcJC1a9cakox58+YZhmEYdrvdaNKkifHggw9WnTN37lxDkjFz5sxqXzts2DCjefPmVZ9/+umnhtVqNZYsWVLtvHfeeceQZCxbtqzqmCTDarUaSUlJp9VUWFhY7fOSkhIjMTHRGDBgQNWxdevWGZKMhx56qNq5t95662kh6I477jCio6ONzMzMauf+4Q9/MIKDg0+73pmkpqYa/v7+RmhoqGGz2YwtW7YYhmEYS5YsMSQZU6ZMqXb+nDlzTjt+puvcfffdhp+fn3HixImqY/369TMkGe+8885p5/82YFR+b2bPnl3tvA4dOlQ772zOFoLatWtXLfS+/vrrhqSq120YvwaTo0eP/m6NZWVlpwXoY8eOGZGRkcbtt99edezo0aOnfe9+e61KGzduNCQZd955Z7XzHnvsMUOSsWDBgqpjsbGxhiTjl19+qTqWkZFheHt7G48++ujZ/mgAwOmwHA4AHGTKlCmKjIxU//79JVUsU7v++uv1xRdfVC3TGjBggBo2bKgvv/yy6uuOHTumefPm6frrr6869vXXX6tdu3Zq27atMjMzqz4GDBggSdWWP0lSv379FB8ff1pNvr6+1a6Tm5uryy67TOvXr686Xrl07s9//nO1r73//vurfW4YhqZNm6aRI0fKMIxqdQ0ZMkS5ubnVnvdsYmNjNWHCBGVnZ+uRRx5RYmJi1WsODg7WoEGDqj13586dFRAQUO01n/q68vPzlZmZqcsuu0yFhYVKTk6udj1vb+8zLkH8rYEDB6pRo0bVljBu3bpVmzdv1k033XTOrz+b2267TV5eXlWfX3bZZZKkvXv31vi5PDw8qp7LbrcrOztbZWVl6tKly3n92Z/Jjz/+KEl65JFHqh1/9NFHJem0jn3x8fFVr0GqWMLXpk2bC3o9AGAWT7MLAID6oLy8XF988YX69++vlJSUquPdu3fXyy+/rPnz52vw4MHy9PTUNddco6lTp6q4uFje3t6aPn26SktLq4WgXbt2afv27VX3dvxW5Y30leLi4s543qxZs/TPf/5TGzdurHZ/x6n3hOzbt09Wq/W052jZsmW1z48ePaqcnBz973//O2sL59/WdTZdu3aVJHXp0qXq2K5du5Sbm6uIiIhzPndSUpKeeOIJLViwQHl5edXOy83NrfZ548aNq4WQs7Farbrxxhv19ttvq7CwUH5+fpoyZYp8fHx03XXXndfrOpOmTZtW+zwkJESSTrvP6Xx9/PHHevnll5WcnKzS0tKq42cbA+dS+f3/7fc7KipKDRo00L59+6od/+3rkSpe04W+HgAwAyEIABxgwYIFSk9P1xdffKEvvvjitMenTJmiwYMHS5L+8Ic/6N1339Xs2bM1evRoffXVV2rbtq06duxYdb7dblf79u31yiuvnPF6MTEx1T4/dWak0pIlSzRq1Cj17dtXb731lqKjo2Wz2fThhx9q6tSpNX6NdrtdknTTTTdp/PjxZzynQ4cONX7eU58/IiLirB31KgNhTk6O+vXrp6CgID399NNq0aKFfHx8tH79ev3tb3+rqrPSmf5szuaWW27Rv//9b3333Xe64YYbNHXqVI0YMULBwcEX/Lo8PDzOeNw4pUPe+frss8906623avTo0frLX/6iiIiIqqYNe/bsueAaJZ33BqqOfD0AYBZCEAA4wJQpUxQREaE333zztMemT5+ub7/9Vu+88458fX3Vt29fRUdH68svv1SfPn20YMEC/eMf/6j2NS1atNCmTZt0xRVXnPeb09+aNm2afHx8NHfuXHl7e1cd//DDD6udFxsbK7vdrpSUFLVq1arq+G/3OAoPD1dgYKDKy8s1cODAC6rp97Ro0UI///yzevfu/bvBZdGiRcrKytL06dPVt2/fquOnzsBdqMTERF166aWaMmWKmjRporS0NL3xxhsX/byO8s0336h58+aaPn16tXFR2V2uUk3GTOX3f9euXWrXrl3V8SNHjignJ0exsbEXXzgAOBnuCQKAi1RUVKTp06drxIgRuvbaa0/7uO+++5Sfn68ZM2ZIqlh2de2112rmzJn69NNPVVZWVm0pnCSNHTtWBw8e1HvvvXfG6x0/fvycdXl4eMhisVRrG52amqrvvvuu2nlDhgyRJL311lvVjv/2zb+Hh4euueYaTZs2TVu3bj3tekePHj1nTb9n7NixKi8v1zPPPHPaY2VlZcrJyamqQ6o+81BSUnJa/Rfq5ptv1k8//aTXXntNYWFhGjp0qEOe1xHO9NpXrVqlFStWVDvPz89Pkqr+zH7PsGHDJEmvvfZateOVs5DuuoEtgPqNmSAAuEgzZsxQfn6+Ro0adcbHe/ToUbVxamXYuf766/XGG29owoQJat++fbXfwEsVb8S/+uor/elPf9LChQvVu3dvlZeXKzk5WV999ZXmzp1b7X6aMxk+fLheeeUVXXnllRo3bpwyMjL05ptvqmXLltq8eXPVeZ07d9Y111yj1157TVlZWerRo4cWL16snTt3Sqo+q/D8889r4cKF6t69u+666y7Fx8crOztb69ev188//6zs7OwL+jOUKpo73H333Xruuee0ceNGDR48WDabTbt27dLXX3+t119/Xddee6169eqlkJAQjR8/Xg888IAsFos+/fRThy3HGjdunP7617/q22+/1T333CObzeaQ53WEESNGaPr06RozZoyGDx+ulJQUvfPOO4qPj1dBQUHVeb6+voqPj9eXX36p1q1bKzQ0VImJiVVNKE7VsWNHjR8/Xv/73/+qlhquXr1aH3/8sUaPHl3V6AMA6hNCEABcpMqb5wcNGnTGx61Wq4YPH64pU6YoKytLYWFh6tWrl2JiYrR///7TZoEqv+a7777Tq6++qk8++UTffvut/Pz81Lx5cz344INq3br1OesaMGCAJk+erOeff14PPfSQ4uLi9MILLyg1NbVaCJKkTz75RFFRUfr888/17bffauDAgfryyy/Vpk0b+fj4VJ0XGRmp1atX6+mnn9b06dP11ltvKSwsTAkJCXrhhRdq+Cd3unfeeUedO3fWu+++q7///e/y9PRUs2bNdNNNN6l3796SpLCwMM2aNUuPPvqonnjiCYWEhOimm27SFVdcUTWrdTEiIyM1ePBg/fjjj7r55psv+vkc6dZbb9Xhw4f17rvvau7cuYqPj9dnn32mr7/+WosWLap27vvvv6/7779fDz/8sEpKSjRhwoQzhqDKc5s3b66PPvpI3377raKiovT444+ftswOAOoLi8GdjACAM9i4caMuvfRSffbZZ7rxxhvNLqdOjRkzRlu2bDntvigAQP3APUEAABUVFZ127LXXXpPVaq3WfMAdpKen64cffnC6WSAAgOOwHA4AoBdffFHr1q1T//795enpqdmzZ2v27Nn64x//eFo77voqJSVFy5Yt0/vvvy+bzaa7777b7JIAALWEEAQAUK9evTRv3jw988wzKigoUNOmTTVx4sTTWnfXZ4sXL9Ztt92mpk2b6uOPP1ZUVJTZJQEAagn3BAEAAABwK9wTBAAAAMCtEIIAAAAAuBWXvifIbrfr0KFDCgwMrLaZHwAAAAD3YhiG8vPz1ahRI1mtvz/X49Ih6NChQ27TtQgAAADAue3fv19NmjT53XNcOgQFBgZKqnihQUFBptZSWlqqn376SYMHD5bNZjO1FrgHxhzqGmMOdYnxhrrGmHN9eXl5iomJqcoIv8elQ1DlErigoCCnCEF+fn4KCgriLw7qBGMOdY0xh7rEeENdY8zVH+dzmwyN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uAnGHOoaYw51ifGGusaYcy8u3RgBAAAAAGqKmSAAAAAAboUQBAAAAMCtEIIAAAAAuBVCEAAAAAC3Qgg6xXPPPaeuXbsqMDBQERERGj16tHbs2FHtnBMnTujee+9VWFiYAgICdM011+jIkSPVznnggQfUuXNneXt765JLLvnda+7evVuBgYFq0KCBg18NnF1djjfDMPTSSy+pdevW8vb2VuPGjfXss8/W1kuDk6rLMTd37lz16NFDgYGBCg8P1zXXXKPU1NRaemVwVo4Yc5s2bdINN9ygmJgY+fr6ql27dnr99ddPu9aiRYvUqVMneXt7q2XLlvroo49q++XBydTVeJs+fboGDRqk8PBwBQUFqWfPnpo7d26dvEY4DiHoFIsXL9a9996rlStXat68eSotLdXgwYN1/PjxqnMefvhhzZw5U19//bUWL16sQ4cO6eqrrz7tuW6//XZdf/31v3u90tJS3XDDDbrssssc/lrg/OpyvD344IN6//339dJLLyk5OVkzZsxQt27dauV1wXnV1ZhLSUnRVVddpQEDBmjjxo2aO3euMjMzz/g8qN8cMebWrVuniIgIffbZZ0pKStI//vEPPf744/rvf/9bdU5KSoqGDx+u/v37a+PGjXrooYd055138sbUzdTVePvll180aNAg/fjjj1q3bp369++vkSNHasOGDXX6enGRDJxVRkaGIclYvHixYRiGkZOTY9hsNuPrr7+uOmf79u2GJGPFihWnff2ECROMjh07nvX5//rXvxo33XST8eGHHxrBwcGOLh8uprbG27Zt2wxPT08jOTm51mqHa6qtMff1118bnp6eRnl5edWxGTNmGBaLxSgpKXH8C4HLuNgxV+nPf/6z0b9//6rP//rXvxoJCQnVzrn++uuNIUOGOPgVwJXU1ng7k/j4eGPSpEmOKRx1gpmg35GbmytJCg0NlVTx24HS0lINHDiw6py2bduqadOmWrFiRY2ee8GCBfr666/15ptvOq5guLTaGm8zZ85U8+bNNWvWLMXFxalZs2a68847lZ2d7dgXAJdTW2Ouc+fOslqt+vDDD1VeXq7c3Fx9+umnGjhwoGw2m2NfBFyKo8Zcbm5u1XNI0ooVK6o9hyQNGTKkxv82o36prfH2W3a7Xfn5+b97DpwPIegs7Ha7HnroIfXu3VuJiYmSpMOHD8vLy+u0+3ciIyN1+PDh837urKws3Xrrrfroo48UFBTkyLLhompzvO3du1f79u3T119/rU8++UQfffSR1q1bp2uvvdaRLwEupjbHXFxcnH766Sf9/e9/l7e3txo0aKADBw7oq6++cuRLgItx1Jhbvny5vvzyS/3xj3+sOnb48GFFRkae9hx5eXkqKipy7AuBS6jN8fZbL730kgoKCjR27FiH1Y/a52l2Ac7q3nvv1datW7V06VKHP/ddd92lcePGqW/fvg5/brim2hxvdrtdxcXF+uSTT9S6dWtJ0uTJk9W5c2ft2LFDbdq0cfg14fxqc8wdPnxYd911l8aPH68bbrhB+fn5euqpp3Tttddq3rx5slgsDr8mnJ8jxtzWrVt11VVXacKECRo8eLADq0N9U1fjberUqZo0aZK+//57RUREXPC1UPeYCTqD++67T7NmzdLChQvVpEmTquNRUVEqKSlRTk5OtfOPHDmiqKio837+BQsW6KWXXpKnp6c8PT11xx13KDc3V56envrggw8c9TLgImp7vEVHR8vT07MqAElSu3btJElpaWkXVzxcUm2PuTfffFPBwcF68cUXdemll6pv37767LPPNH/+fK1atcpRLwMuxBFjbtu2bbriiiv0xz/+UU888US1x6Kiok7rYnjkyBEFBQXJ19fXsS8GTq+2x1ulL774Qnfeeae++uqr05ZjwvkRgk5hGIbuu+8+ffvtt1qwYIHi4uKqPd65c2fZbDbNnz+/6tiOHTuUlpamnj17nvd1VqxYoY0bN1Z9PP300woMDNTGjRs1ZswYh70eOLe6Gm+9e/dWWVmZ9uzZU3Vs586dkqTY2NiLfBVwJXU15goLC2W1Vv/nxcPDQ1LFzCTch6PGXFJSkvr376/x48efsb1/z549qz2HJM2bN69G4xaur67GmyR9/vnnuu222/T5559r+PDhtfOCULtMbcvgZO655x4jODjYWLRokZGenl71UVhYWHXOn/70J6Np06bGggULjLVr1xo9e/Y0evbsWe15du3aZWzYsMG4++67jdatWxsbNmwwNmzYYBQXF5/xunSHc091Nd7Ky8uNTp06GX379jXWr19vrF271ujevbsxaNCgOn29MF9djbn58+cbFovFmDRpkrFz505j3bp1xpAhQ4zY2Nhq10L954gxt2XLFiM8PNy46aabqj1HRkZG1Tl79+41/Pz8jL/85S/G9u3bjTfffNPw8PAw5syZU6evF+aqq/E2ZcoUw9PT03jzzTernZOTk1OnrxcXhxB0Ckln/Pjwww+rzikqKjL+/Oc/GyEhIYafn58xZswYIz09vdrz9OvX74zPk5KScsbrEoLcU12Ot4MHDxpXX321ERAQYERGRhq33nqrkZWVVUevFM6iLsfc559/blx66aWGv7+/ER4ebowaNcrYvn17Hb1SOAtHjLkJEyac8TliY2OrXWvhwoXGJZdcYnh5eRnNmzevdg24h7oab2f7GTh+/Pi6e7G4aBbDMAzHzCkBAAAAgPPjniAAAAAAboUQBAAAAMCtEIIAAAAAuBVCEAAAAAC3QggCAAAA4FYIQQAAAADcCiEIAAAAgFshBAEAAABwK4QgAAAAAG6FEAQAcBqGYWjgwIEaMmTIaY+99dZbatCggQ4cOGBCZQCA+oQQBABwGhaLRR9++KFWrVqld999t+p4SkqK/vrXv+qNN95QkyZNHHrN0tJShz4fAMD5EYIAAE4lJiZGr7/+uh577DGlpKTIMAzdcccdGjx4sC699FINHTpUAQEBioyM1M0336zMzMyqr50zZ4769OmjBg0aKCwsTCNGjNCePXuqHk9NTZXFYtGXX36pfv36ycfHR1OmTDHjZQIATGQxDMMwuwgAAH5r9OjRys3N1dVXX61nnnlGSUlJSkhI0J133qlbbrlFRUVF+tvf/qaysjItWLBAkjRt2jRZLBZ16NBBBQUFeuqpp5SamqqNGzfKarUqNTVVcXFxatasmV5++WVdeuml8vHxUXR0tMmvFgBQlwhBAACnlJGRoYSEBGVnZ2vatGnaunWrlixZorlz51adc+DAAcXExGjHjh1q3br1ac+RmZmp8PBwbdmyRYmJiVUh6LXXXtODDz5Yly8HAOBEWA4HAHBKERERuvvuu9WuXTuNHj1amzZt0sKFCxUQEFD10bZtW0mqWvK2a9cu3XDDDWrevLmCgoLUrFkzSVJaWlq15+7SpUudvhYAgHPxNLsAAADOxtPTU56eFf9UFRQUaOTIkXrhhRdOO69yOdvIkSMVGxur9957T40aNZLdbldiYqJKSkqqne/v71/7xQMAnBYhCADgEjp16qRp06apWbNmVcHoVFlZWdqxY4fee+89XXbZZZKkpUuX1nWZAAAXwHI4AIBLuPfee5Wdna0bbrhBa9as0Z49ezR37lzddtttKi8vV0hIiMLCwvS///1Pu3fv1oIFC/TII4+YXTYAwAkRggAALqFRo0ZatmyZysvLNXjwYLVv314PPfSQGjRoIKvVKqvVqi+++ELr1q1TYmKiHn74Yf373/82u2wAgBOiOxwAAAAAt8JMEAAAAAC3QggCAAAA4FYIQQAAAADcCiEIAAAAgFshBAEAAABwK4QgAAAAAG6FEAQAAADArRCCAAAAALgVQhAAAAAAt0IIAgAAAOBWCEEAAAAA3Mr/A0VAtdI/K4eiAAAAAElFTkSuQmCC\n" + ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } ], "source": [ + "# NBVAL_SKIP\n", "import matplotlib.pyplot as plt\n", "import pandas as pd\n", "\n", @@ -2473,8 +2546,7 @@ "plt.ylabel('Average Inflation')\n", "plt.grid(True)\n", "plt.show()" - ], - "id": "JqBBVLKdIHHq" + ] }, { "cell_type": "markdown", @@ -2522,8 +2594,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "inference> brave_search.call(query=\"NBA Western Conference Finals 2024 teams\")\n", "tool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\n", @@ -2541,7 +2613,7 @@ } ], "source": [ - "from google.colab import userdata\n", + "\n", "from llama_stack_client.lib.agents.agent import Agent\n", "from llama_stack_client.lib.agents.event_logger import EventLogger\n", "from llama_stack_client.types.agent_create_params import AgentConfig\n", @@ -2556,8 +2628,8 @@ ")\n", "agent = Agent(client, agent_config)\n", "user_prompts = [\n", - " \"Which teams played in the NBA western conference finals of 2024\",\n", - " \"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\n", + " # \"Which teams played in the NBA western conference finals of 2024\",\n", + " # \"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\n", " \"What is the British-American kickboxer Andrew Tate's kickboxing name?\",\n", "]\n", "\n", @@ -2602,103 +2674,14 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Getting traces for session_id=44d006af-1394-4832-9799-5f0cb0ca01d6\n" ] }, { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \\\\\"Trapper Keeper\\\\\" of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='44705eaf-b371-4841-b0ee-5eb21a5d7f36', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate | The Real World\\\\\", \\\\\"url\\\\\": \\\\\"https://www.taterealworldofficial.com/about-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Emory Andrew Tate III \u001b[0m\u001b[32m(\u001b[0m\u001b[32mborn December 14, 1986\u001b[0m\u001b[32m)\u001b[0m\u001b[32m is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\\\\\", \\\\\"score\\\\\": 0.8386933, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate - Fight Record - Muay Thai Records\\\\\", \\\\\"url\\\\\": \\\\\"https://muaythairecords.com/fighters/andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\\\\\\\u2032 4\\\\\\\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\\\\\\\u00eet. 14th Mar 2015 -Boxe in D\\\\\\\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\\\\\\\u00eet by decision. ... Name: Andrew Tate\\\\\", \\\\\"score\\\\\": 0.8194462, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \\\\\"Trapper Keeper\\\\\" of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate | The Real World\\\\\", \\\\\"url\\\\\": \\\\\"https://www.taterealworldofficial.com/about-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Emory Andrew Tate III \u001b[0m\u001b[32m(\u001b[0m\u001b[32mborn December 14, 1986\u001b[0m\u001b[32m)\u001b[0m\u001b[32m is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\\\\\", \\\\\"score\\\\\": 0.8386933, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate - Fight Record - Muay Thai Records\\\\\", \\\\\"url\\\\\": \\\\\"https://muaythairecords.com/fighters/andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\\\\\\\u2032 4\\\\\\\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\\\\\\\u00eet. 14th Mar 2015 -Boxe in D\\\\\\\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\\\\\\\u00eet by decision. ... Name: Andrew Tate\\\\\", \\\\\"score\\\\\": 0.8194462, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: Andrew Tate\\'s kickboxing name is \"King Cobra\" or \"Cobra Tate\". tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='1e487e8e-a15f-4137-854a-1d4979a70b8c', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'content: Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \"Trapper Keeper\" of South Park. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[1m]\u001b[0m\n" - ], "text/html": [ "
[\n",
               "{\n",
@@ -2787,12 +2770,102 @@
               "}\n",
               "]\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \\\\\"Trapper Keeper\\\\\" of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='44705eaf-b371-4841-b0ee-5eb21a5d7f36', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate | The Real World\\\\\", \\\\\"url\\\\\": \\\\\"https://www.taterealworldofficial.com/about-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Emory Andrew Tate III \u001b[0m\u001b[32m(\u001b[0m\u001b[32mborn December 14, 1986\u001b[0m\u001b[32m)\u001b[0m\u001b[32m is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\\\\\", \\\\\"score\\\\\": 0.8386933, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate - Fight Record - Muay Thai Records\\\\\", \\\\\"url\\\\\": \\\\\"https://muaythairecords.com/fighters/andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\\\\\\\u2032 4\\\\\\\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\\\\\\\u00eet. 14th Mar 2015 -Boxe in D\\\\\\\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\\\\\\\u00eet by decision. ... Name: Andrew Tate\\\\\", \\\\\"score\\\\\": 0.8194462, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \\\\\"Trapper Keeper\\\\\" of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate | The Real World\\\\\", \\\\\"url\\\\\": \\\\\"https://www.taterealworldofficial.com/about-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Emory Andrew Tate III \u001b[0m\u001b[32m(\u001b[0m\u001b[32mborn December 14, 1986\u001b[0m\u001b[32m)\u001b[0m\u001b[32m is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\\\\\", \\\\\"score\\\\\": 0.8386933, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate - Fight Record - Muay Thai Records\\\\\", \\\\\"url\\\\\": \\\\\"https://muaythairecords.com/fighters/andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\\\\\\\u2032 4\\\\\\\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\\\\\\\u00eet. 14th Mar 2015 -Boxe in D\\\\\\\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\\\\\\\u00eet by decision. ... Name: Andrew Tate\\\\\", \\\\\"score\\\\\": 0.8194462, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: Andrew Tate\\'s kickboxing name is \"King Cobra\" or \"Cobra Tate\". tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='1e487e8e-a15f-4137-854a-1d4979a70b8c', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'content: Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \"Trapper Keeper\" of South Park. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[1m]\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } ], "source": [ + "# NBVAL_SKIP\n", "print(f\"Getting traces for session_id={session_id}\")\n", "import json\n", "\n", @@ -2839,27 +2912,7 @@ }, "outputs": [ { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='44705eaf-b371-4841-b0ee-5eb21a5d7f36', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='1e487e8e-a15f-4137-854a-1d4979a70b8c', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m: \u001b[32m'brave_search'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[1m]\u001b[0m\n" - ], "text/html": [ "
[\n",
               "{\n",
@@ -2879,23 +2932,32 @@
               "}\n",
               "]\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='44705eaf-b371-4841-b0ee-5eb21a5d7f36', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='1e487e8e-a15f-4137-854a-1d4979a70b8c', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m: \u001b[32m'brave_search'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[1m]\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" }, { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1;35mScoringScoreResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mresults\u001b[0m=\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'basic::subset_of'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m3.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m3\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ], "text/html": [ "
ScoringScoreResponse(\n",
               "results={\n",
@@ -2906,12 +2968,25 @@
               "}\n",
               ")\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1;35mScoringScoreResponse\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mresults\u001b[0m=\u001b[1m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'basic::subset_of'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m3.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m3\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } ], "source": [ + "# NBVAL_SKIP\n", + "\n", "# post-process telemetry spance and prepare data for eval\n", "# in this case, we want to assert that all user prompts is followed by a tool call\n", "import ast\n", @@ -2920,6 +2995,7 @@ "eval_rows = []\n", "\n", "for log in agent_logs:\n", + " print(log)\n", " last_msg = log[\"input\"][-1]\n", " if '\"role\":\"user\"' in last_msg:\n", " eval_rows.append(\n", @@ -2968,27 +3044,7 @@ }, "outputs": [ { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1;35mScoringScoreResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mresults\u001b[0m=\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'llm-as-judge::base'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ │ │ \u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ │ \u001b[0m\u001b[32m'judge_feedback'\u001b[0m: \u001b[32m\"Answer: B, Explanation: The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE as it provides more detailed information about the topics related to LoRA \u001b[0m\u001b[32m(\u001b[0m\u001b[32malthough it does list more than one topic as does not exactly follow the desired format of only giving one 'topic', while the EXPECTED_RESPONSE simply lists 'LoRA'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m.\"\u001b[0m\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m]\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'basic::subset_of'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ], "text/html": [ "
ScoringScoreResponse(\n",
               "results={\n",
@@ -3008,9 +3064,29 @@
               "}\n",
               ")\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1;35mScoringScoreResponse\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[33mresults\u001b[0m=\u001b[1m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'llm-as-judge::base'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\n", + "\u001b[2;32m│ │ │ │ │ \u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ │ \u001b[0m\u001b[32m'judge_feedback'\u001b[0m: \u001b[32m\"Answer: B, Explanation: The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE as it provides more detailed information about the topics related to LoRA \u001b[0m\u001b[32m(\u001b[0m\u001b[32malthough it does list more than one topic as does not exactly follow the desired format of only giving one 'topic', while the EXPECTED_RESPONSE simply lists 'LoRA'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m.\"\u001b[0m\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'basic::subset_of'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m1\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[1m)\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } ], "source": [ @@ -3100,202 +3176,10 @@ }, "widgets": { "application/vnd.jupyter.widget-state+json": { - "88f0c88612bb45d59f07e93567cc0e14": { + "01b3e7803d1946118d27acda0c067da2": { "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_9b24a82117e1482a8f6665978e84089c", - "IPY_MODEL_8e75bf7cac454eeabd5ce47a1e981c68", - "IPY_MODEL_fc272883566541108f83117ccd146a21" - ], - "layout": "IPY_MODEL_2e27a025a416434f8ab3b63049626d11" - } - }, - "9b24a82117e1482a8f6665978e84089c": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_3a46a46bc8124a92b27aef43cbc009b6", - "placeholder": "​", - "style": "IPY_MODEL_4ad6bc0cca62446d8faf19a341bfa86f", - "value": "modules.json: 100%" - } - }, - "8e75bf7cac454eeabd5ce47a1e981c68": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_6437c99289f947449f7d2964288973e5", - "max": 349, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_e2f7dea8fc744537b42d0f1a85a73eb4", - "value": 349 - } - }, - "fc272883566541108f83117ccd146a21": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1377d2160344430da8f29a50d113a288", - "placeholder": "​", - "style": "IPY_MODEL_0c0b30e126724f9282ac5acbcb4581db", - "value": " 349/349 [00:00<00:00, 7.72kB/s]" - } - }, - "2e27a025a416434f8ab3b63049626d11": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3a46a46bc8124a92b27aef43cbc009b6": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4ad6bc0cca62446d8faf19a341bfa86f": { - "model_module": "@jupyter-widgets/controls", "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -3307,1430 +3191,10 @@ "description_width": "" } }, - "6437c99289f947449f7d2964288973e5": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "e2f7dea8fc744537b42d0f1a85a73eb4": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "1377d2160344430da8f29a50d113a288": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "0c0b30e126724f9282ac5acbcb4581db": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "895efd0b6d9f4b319159703d965d1966": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_dece6dff65394a5f93585c73359d4dad", - "IPY_MODEL_1030c0848635497681cc9ff0c344fb1a", - "IPY_MODEL_fa6ecaab432347de8427b9b5ac3d4524" - ], - "layout": "IPY_MODEL_5effefa8e3764e3aaff57fe0197a7c96" - } - }, - "dece6dff65394a5f93585c73359d4dad": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1756eceba2c34c1ca182b7db465e95ce", - "placeholder": "​", - "style": "IPY_MODEL_0fd62e56e0bb41a996c04e63381d2a29", - "value": "config_sentence_transformers.json: 100%" - } - }, - "1030c0848635497681cc9ff0c344fb1a": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_29badfc2eb0345d38d7cfc6c7f8bb1a8", - "max": 116, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_e64cedb4560a43d8a43f36002087ac30", - "value": 116 - } - }, - "fa6ecaab432347de8427b9b5ac3d4524": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_45aadb26b382460eb5b6b147509fb75a", - "placeholder": "​", - "style": "IPY_MODEL_130f2f5840764e8dbd573cc8a6ea6f5f", - "value": " 116/116 [00:00<00:00, 3.35kB/s]" - } - }, - "5effefa8e3764e3aaff57fe0197a7c96": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1756eceba2c34c1ca182b7db465e95ce": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "0fd62e56e0bb41a996c04e63381d2a29": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "29badfc2eb0345d38d7cfc6c7f8bb1a8": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "e64cedb4560a43d8a43f36002087ac30": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "45aadb26b382460eb5b6b147509fb75a": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "130f2f5840764e8dbd573cc8a6ea6f5f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "9ee45247ec144bb3aafe4208f316063f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_da330e0999cb4c3c91a1cb1026304568", - "IPY_MODEL_ff58a5381fb74cb1b9efc10f5c2738d6", - "IPY_MODEL_18ed62b1d4594ed9a2651fa5df046efc" - ], - "layout": "IPY_MODEL_4004cda1d84949f5a380536f8a9d0274" - } - }, - "da330e0999cb4c3c91a1cb1026304568": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_54bddcf41c5641b7a56c981aadb62ef1", - "placeholder": "​", - "style": "IPY_MODEL_a9a0d8415d9d4e98a3f02ae8ec1053da", - "value": "README.md: 100%" - } - }, - "ff58a5381fb74cb1b9efc10f5c2738d6": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_cceff1126242494bab432205c7ac7345", - "max": 10659, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_e6e53c439dab4639adc1c3c873602476", - "value": 10659 - } - }, - "18ed62b1d4594ed9a2651fa5df046efc": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_95db8eab3f964edf99038ad53f41fabc", - "placeholder": "​", - "style": "IPY_MODEL_52f1d69c6cd04816b6f34657893ae32b", - "value": " 10.7k/10.7k [00:00<00:00, 223kB/s]" - } - }, - "4004cda1d84949f5a380536f8a9d0274": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "54bddcf41c5641b7a56c981aadb62ef1": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a9a0d8415d9d4e98a3f02ae8ec1053da": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "cceff1126242494bab432205c7ac7345": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "e6e53c439dab4639adc1c3c873602476": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "95db8eab3f964edf99038ad53f41fabc": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "52f1d69c6cd04816b6f34657893ae32b": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "b79a1dfcf2904bcba332569dbf351f34": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_7363b1a9a1b54a57bf15357e897128fd", - "IPY_MODEL_3ac596104cdc4439b3980f7ce66ad080", - "IPY_MODEL_5c9ec25994914acd8e13866b3eb943e1" - ], - "layout": "IPY_MODEL_38a958036c6e4155815a8169f1be1e53" - } - }, - "7363b1a9a1b54a57bf15357e897128fd": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_cf5113a647ce45c4a3a523361aa3b5af", - "placeholder": "​", - "style": "IPY_MODEL_da8c20a65ba541bda058614849d5cfe2", - "value": "sentence_bert_config.json: 100%" - } - }, - "3ac596104cdc4439b3980f7ce66ad080": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_40e9f20d74374b0e82c653caa0559d04", - "max": 53, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_f46cfc9237e64db6be2ec6529b61ec88", - "value": 53 - } - }, - "5c9ec25994914acd8e13866b3eb943e1": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_dc04575da46540d4ad3a708e58f0de6a", - "placeholder": "​", - "style": "IPY_MODEL_24c0be775e474517a7be49d187822bd0", - "value": " 53.0/53.0 [00:00<00:00, 3.84kB/s]" - } - }, - "38a958036c6e4155815a8169f1be1e53": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "cf5113a647ce45c4a3a523361aa3b5af": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "da8c20a65ba541bda058614849d5cfe2": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "40e9f20d74374b0e82c653caa0559d04": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f46cfc9237e64db6be2ec6529b61ec88": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "dc04575da46540d4ad3a708e58f0de6a": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "24c0be775e474517a7be49d187822bd0": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "111184729957441d9d1f3d404bd82757": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_be060f9d7a664c17a80510f447c0bee3", - "IPY_MODEL_228445132e5f4b2ca793f4beeeca4426", - "IPY_MODEL_b96a2e34a2af435b9705550fe564591d" - ], - "layout": "IPY_MODEL_1f1cdac013af4559889f15eebac5256a" - } - }, - "be060f9d7a664c17a80510f447c0bee3": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_834ae2d249b94be6bbe5349509536a4b", - "placeholder": "​", - "style": "IPY_MODEL_509863a58de74b07b813aa83ffa4a507", - "value": "config.json: 100%" - } - }, - "228445132e5f4b2ca793f4beeeca4426": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_48a5b775a4324da791603b83d61be7d1", - "max": 612, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_02b60dad91c7482ba70cf8bb954bc4eb", - "value": 612 - } - }, - "b96a2e34a2af435b9705550fe564591d": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_2bfb0fb5506d4285918a9c94af9ab5d1", - "placeholder": "​", - "style": "IPY_MODEL_0f699b0f99484a8ba2eb17bb1d621c5a", - "value": " 612/612 [00:00<00:00, 47.5kB/s]" - } - }, - "1f1cdac013af4559889f15eebac5256a": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "834ae2d249b94be6bbe5349509536a4b": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "509863a58de74b07b813aa83ffa4a507": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "48a5b775a4324da791603b83d61be7d1": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "02b60dad91c7482ba70cf8bb954bc4eb": { "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -4743,10 +3207,226 @@ "description_width": "" } }, - "2bfb0fb5506d4285918a9c94af9ab5d1": { + "02baf670942347d69c290452de8641e4": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "03402ad03418435ca7a550e3246cd300": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_9df914248c214597bed7d7980c7a0afe", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_4709067f3f554b93b3ef35e3f58cbf85", + "value": 1 + } + }, + "0ac8e976a32c4f5989392b8088546e00": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0b64892a98d14a3b85b128df77d8e7d6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_542aa4a847cf4a66a4b3fc93c241363b", + "placeholder": "​", + "style": "IPY_MODEL_8c0d69b735c94b719160d39256c643cc", + "value": " 112/112 [00:00<00:00, 6.51kB/s]" + } + }, + "0c0b30e126724f9282ac5acbcb4581db": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "0c2e30d78c234b1b8098d879442d3bac": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0f3bbf28fbed4e97b660bbf3c66a214a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -4797,8 +3477,8 @@ }, "0f699b0f99484a8ba2eb17bb1d621c5a": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -4810,53 +3490,25 @@ "description_width": "" } }, - "c6f34317390e4f90b16235f2ae84a981": { + "0fd62e56e0bb41a996c04e63381d2a29": { "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", "state": { - "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", + "_model_name": "DescriptionStyleModel", "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_3da95c8814f34472a181ce7687f9e15e", - "IPY_MODEL_4d1c2de4c1354ef0b84c54c447141707", - "IPY_MODEL_31ab98e0e375416b83b36a98d4958f57" - ], - "layout": "IPY_MODEL_8b9ebe06b4e045a29269128ec97d9f62" + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" } }, - "3da95c8814f34472a181ce7687f9e15e": { + "1030c0848635497681cc9ff0c344fb1a": { "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_53a46fe254924e78876db6dd2e1b7123", - "placeholder": "​", - "style": "IPY_MODEL_f2ce01983f0a4f12b318e6d29f1dd4a1", - "value": "model.safetensors: 100%" - } - }, - "4d1c2de4c1354ef0b84c54c447141707": { - "model_module": "@jupyter-widgets/controls", "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -4869,2433 +3521,18 @@ "bar_style": "success", "description": "", "description_tooltip": null, - "layout": "IPY_MODEL_1b7af9f7204547b8b4a718a780af0ded", - "max": 90868376, + "layout": "IPY_MODEL_29badfc2eb0345d38d7cfc6c7f8bb1a8", + "max": 116, "min": 0, "orientation": "horizontal", - "style": "IPY_MODEL_a4bb5a59d1324585b0a34c9bb2820b7f", - "value": 90868376 - } - }, - "31ab98e0e375416b83b36a98d4958f57": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_90c2e0e012a94521b9f5cb24924771d8", - "placeholder": "​", - "style": "IPY_MODEL_2563a4677dde47d0a2f7fba5c5dde358", - "value": " 90.9M/90.9M [00:00<00:00, 223MB/s]" - } - }, - "8b9ebe06b4e045a29269128ec97d9f62": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "53a46fe254924e78876db6dd2e1b7123": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f2ce01983f0a4f12b318e6d29f1dd4a1": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "1b7af9f7204547b8b4a718a780af0ded": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a4bb5a59d1324585b0a34c9bb2820b7f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "90c2e0e012a94521b9f5cb24924771d8": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2563a4677dde47d0a2f7fba5c5dde358": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "5023c2b8cf9846069d116237826fed7f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_960c2f44166b4ac7910af6512832186f", - "IPY_MODEL_309ea9620a674088a5207206d9a52d54", - "IPY_MODEL_1c86d856083c4ef99976849c7a1c9100" - ], - "layout": "IPY_MODEL_5d9bf2102da143c1b9e1483e05add4e5" - } - }, - "960c2f44166b4ac7910af6512832186f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_85569eaf3ae3488b808131cd460f6514", - "placeholder": "​", - "style": "IPY_MODEL_3015bc3ce98a4221a9dd3be92481435d", - "value": "tokenizer_config.json: 100%" - } - }, - "309ea9620a674088a5207206d9a52d54": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_4d7b0983b97f48b2a333d5b2a4ec50a8", - "max": 350, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_e834a64e49534c3586cb77f4ec5eab2d", - "value": 350 - } - }, - "1c86d856083c4ef99976849c7a1c9100": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_67f82b82ebb74d0fb3c68b9c8c57d690", - "placeholder": "​", - "style": "IPY_MODEL_b710cb57f19d4490a740c060e8a83b90", - "value": " 350/350 [00:00<00:00, 26.0kB/s]" - } - }, - "5d9bf2102da143c1b9e1483e05add4e5": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "85569eaf3ae3488b808131cd460f6514": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3015bc3ce98a4221a9dd3be92481435d": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "4d7b0983b97f48b2a333d5b2a4ec50a8": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "e834a64e49534c3586cb77f4ec5eab2d": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "67f82b82ebb74d0fb3c68b9c8c57d690": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b710cb57f19d4490a740c060e8a83b90": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "713c09d1275a43b0af7c2ae8e126517f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_b62fe08114f549ea99808e8df95c7cad", - "IPY_MODEL_af722d177320422e97c679b24cb754f6", - "IPY_MODEL_487477e023b64947bf42f83dc6275ef1" - ], - "layout": "IPY_MODEL_bcf0d3af3bc0439e97023937852941e9" - } - }, - "b62fe08114f549ea99808e8df95c7cad": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_d83a1e1e678e4efd83115f9aee0ffc8d", - "placeholder": "​", - "style": "IPY_MODEL_f210583576594e759387fc704695ad09", - "value": "vocab.txt: 100%" - } - }, - "af722d177320422e97c679b24cb754f6": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_91e103573c034ceda689047c61294b17", - "max": 231508, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_b9eac61fb55342f4bf9834f321899836", - "value": 231508 - } - }, - "487477e023b64947bf42f83dc6275ef1": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_a92a7bce961e4291b126fda3c540636b", - "placeholder": "​", - "style": "IPY_MODEL_01b3e7803d1946118d27acda0c067da2", - "value": " 232k/232k [00:00<00:00, 550kB/s]" - } - }, - "bcf0d3af3bc0439e97023937852941e9": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "d83a1e1e678e4efd83115f9aee0ffc8d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f210583576594e759387fc704695ad09": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "91e103573c034ceda689047c61294b17": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b9eac61fb55342f4bf9834f321899836": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "a92a7bce961e4291b126fda3c540636b": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "01b3e7803d1946118d27acda0c067da2": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "f097b32928f246de9b01fea6f9b092f7": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_35e10db3906248ffa8ab955d2f53bd75", - "IPY_MODEL_80e884cae6ea42eaa37f028120963355", - "IPY_MODEL_25821e7aef4e481bbdf3b4698ce3c277" - ], - "layout": "IPY_MODEL_916190b4615e4c5c9f3e55c0804a3502" - } - }, - "35e10db3906248ffa8ab955d2f53bd75": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1f1dc0d20cae46feb372203aea6458a0", - "placeholder": "​", - "style": "IPY_MODEL_43feace0290a47c0b06c3a1c08cc70a9", - "value": "tokenizer.json: 100%" - } - }, - "80e884cae6ea42eaa37f028120963355": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_9f185162847f4cb2828af81c92116582", - "max": 466247, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_3a649adc22694036b35bab04ff03d338", - "value": 466247 - } - }, - "25821e7aef4e481bbdf3b4698ce3c277": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_7daef1502e2a4140ac021b3b3a6aa12d", - "placeholder": "​", - "style": "IPY_MODEL_1307ef0325bb433d8a1bcc653c7fb291", - "value": " 466k/466k [00:00<00:00, 2.16MB/s]" - } - }, - "916190b4615e4c5c9f3e55c0804a3502": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1f1dc0d20cae46feb372203aea6458a0": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "43feace0290a47c0b06c3a1c08cc70a9": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "9f185162847f4cb2828af81c92116582": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3a649adc22694036b35bab04ff03d338": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "7daef1502e2a4140ac021b3b3a6aa12d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1307ef0325bb433d8a1bcc653c7fb291": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "f01d7a1404a943a08c84adce14a262c7": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_f15cdedf8e7b4a44993644a5ff070e78", - "IPY_MODEL_b7f9a3c97f2043f380bdc1827961c649", - "IPY_MODEL_0b64892a98d14a3b85b128df77d8e7d6" - ], - "layout": "IPY_MODEL_8de1cba3a7c0422eb2a21e3f8b2059c7" - } - }, - "f15cdedf8e7b4a44993644a5ff070e78": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_a0639d5360044f97ac5b9374c735ff4b", - "placeholder": "​", - "style": "IPY_MODEL_9b11eaf2d50a447384b75eb7f73829eb", - "value": "special_tokens_map.json: 100%" - } - }, - "b7f9a3c97f2043f380bdc1827961c649": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_8ab411217bfd486ca3fb8b885fff4690", - "max": 112, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_c80ea8c54211427087712b5500e26edf", - "value": 112 - } - }, - "0b64892a98d14a3b85b128df77d8e7d6": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_542aa4a847cf4a66a4b3fc93c241363b", - "placeholder": "​", - "style": "IPY_MODEL_8c0d69b735c94b719160d39256c643cc", - "value": " 112/112 [00:00<00:00, 6.51kB/s]" - } - }, - "8de1cba3a7c0422eb2a21e3f8b2059c7": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a0639d5360044f97ac5b9374c735ff4b": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9b11eaf2d50a447384b75eb7f73829eb": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "8ab411217bfd486ca3fb8b885fff4690": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "c80ea8c54211427087712b5500e26edf": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "542aa4a847cf4a66a4b3fc93c241363b": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "8c0d69b735c94b719160d39256c643cc": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "3c868641db934c67a44e1d26e1a17756": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_a72d01788b484bbeb4375aac3ceadf34", - "IPY_MODEL_366add01dc734455a384460c97491215", - "IPY_MODEL_70accb92e645435b8f1e0c48538f7473" - ], - "layout": "IPY_MODEL_628848757fcf443e806a8f25013cc2b5" - } - }, - "a72d01788b484bbeb4375aac3ceadf34": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_ebf411690c844daf89b87c120e3cb67e", - "placeholder": "​", - "style": "IPY_MODEL_79b9fb75dc1d486c9fc881a90b6f1060", - "value": "1_Pooling/config.json: 100%" - } - }, - "366add01dc734455a384460c97491215": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_0f3bbf28fbed4e97b660bbf3c66a214a", - "max": 190, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_a4b2220ed47f4f85b3f991c92de98964", - "value": 190 - } - }, - "70accb92e645435b8f1e0c48538f7473": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_b6a505e6c863409db1b906423f99125a", - "placeholder": "​", - "style": "IPY_MODEL_d9560d20106a42ec904e7e315f99ff01", - "value": " 190/190 [00:00<00:00, 9.18kB/s]" - } - }, - "628848757fcf443e806a8f25013cc2b5": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "ebf411690c844daf89b87c120e3cb67e": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "79b9fb75dc1d486c9fc881a90b6f1060": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "0f3bbf28fbed4e97b660bbf3c66a214a": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a4b2220ed47f4f85b3f991c92de98964": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "b6a505e6c863409db1b906423f99125a": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "d9560d20106a42ec904e7e315f99ff01": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "edc4d84302f746d39a43e8107af6b67b": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_980292182c7144e194604c13ac544a26", - "IPY_MODEL_8dee873065a047799a04e49ab791e449", - "IPY_MODEL_29683ef34d5646c687118a2a0cdec6d4" - ], - "layout": "IPY_MODEL_3ec694106303491ea112a257309bc69c" - } - }, - "980292182c7144e194604c13ac544a26": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_288c9da81b3c4d80a4959753da973f58", - "placeholder": "​", - "style": "IPY_MODEL_cf453a1ed54645aba656f9a3f1461e69", - "value": "Batches: 100%" - } - }, - "8dee873065a047799a04e49ab791e449": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_ec747bd7c37c45298896c513634cd59a", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_5a620017a5384af1a056de687b2670db", - "value": 1 - } - }, - "29683ef34d5646c687118a2a0cdec6d4": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_8d370762fafd4d7887ff68ea8279d083", - "placeholder": "​", - "style": "IPY_MODEL_b6a0eb553b024a71b737ff47ca8f7633", - "value": " 1/1 [00:01<00:00,  1.24s/it]" - } - }, - "3ec694106303491ea112a257309bc69c": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "288c9da81b3c4d80a4959753da973f58": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "cf453a1ed54645aba656f9a3f1461e69": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "ec747bd7c37c45298896c513634cd59a": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "5a620017a5384af1a056de687b2670db": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "8d370762fafd4d7887ff68ea8279d083": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b6a0eb553b024a71b737ff47ca8f7633": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "2eff72cbd9bb4f1ca77213602caa9417": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_e82b5196209f4b9f919c7abb402a4504", - "IPY_MODEL_fe34706489c14253a5015ff6332ec4e0", - "IPY_MODEL_2574b07e4af24715aa89d048cc84e358" - ], - "layout": "IPY_MODEL_10bc8be68b5545fd8609824b02499ebf" - } - }, - "e82b5196209f4b9f919c7abb402a4504": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_d2473b7a6c5b4483981516af2fc59bde", - "placeholder": "​", - "style": "IPY_MODEL_4282ee7d947e426ba863df9970e82f3f", - "value": "Batches: 100%" - } - }, - "fe34706489c14253a5015ff6332ec4e0": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_cfe6be8fd8254bc084a81b1d06e86ae1", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_1817f6732a5f44c7adc75a644b1acef2", - "value": 1 - } - }, - "2574b07e4af24715aa89d048cc84e358": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_7551b282ef3a4387a801637de2d5c76e", - "placeholder": "​", - "style": "IPY_MODEL_69e5263c812c4542a9e5c31fefaa37fe", - "value": " 1/1 [00:00<00:00, 15.08it/s]" + "style": "IPY_MODEL_e64cedb4560a43d8a43f36002087ac30", + "value": 116 } }, "10bc8be68b5545fd8609824b02499ebf": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -7344,62 +3581,32 @@ "width": null } }, - "d2473b7a6c5b4483981516af2fc59bde": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4282ee7d947e426ba863df9970e82f3f": { + "111184729957441d9d1f3d404bd82757": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_be060f9d7a664c17a80510f447c0bee3", + "IPY_MODEL_228445132e5f4b2ca793f4beeeca4426", + "IPY_MODEL_b96a2e34a2af435b9705550fe564591d" + ], + "layout": "IPY_MODEL_1f1cdac013af4559889f15eebac5256a" + } + }, + "1307ef0325bb433d8a1bcc653c7fb291": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -7411,10 +3618,99 @@ "description_width": "" } }, - "cfe6be8fd8254bc084a81b1d06e86ae1": { + "130f2f5840764e8dbd573cc8a6ea6f5f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "1377d2160344430da8f29a50d113a288": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "15ae23892b634a9f821a8fcee14e500b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_b28d46c2ecdd46b9b3f2da871afbf1cb", + "IPY_MODEL_4b83e3caa8ec47169dca04ee9599adeb", + "IPY_MODEL_c83c23161674484e81f0db9856c23eb6" + ], + "layout": "IPY_MODEL_3ded85d9c34246e88f8ce693eb8025e5" + } + }, + "1756eceba2c34c1ca182b7db465e95ce": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -7465,8 +3761,8 @@ }, "1817f6732a5f44c7adc75a644b1acef2": { "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -7479,10 +3775,31 @@ "description_width": "" } }, - "7551b282ef3a4387a801637de2d5c76e": { + "18ed62b1d4594ed9a2651fa5df046efc": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_95db8eab3f964edf99038ad53f41fabc", + "placeholder": "​", + "style": "IPY_MODEL_52f1d69c6cd04816b6f34657893ae32b", + "value": " 10.7k/10.7k [00:00<00:00, 223kB/s]" + } + }, + "1b7af9f7204547b8b4a718a780af0ded": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -7531,47 +3848,10 @@ "width": null } }, - "69e5263c812c4542a9e5c31fefaa37fe": { + "1c86d856083c4ef99976849c7a1c9100": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "7cc356ed20e94401b72a0e138ad0f5df": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_acd39276db17439798a97abc56460b0f", - "IPY_MODEL_bda474c3b8184597a6a9bc6da0672a50", - "IPY_MODEL_20a66f9de4ed41c7ac9a8e817898ed9e" - ], - "layout": "IPY_MODEL_e662ba10fbae49d9b66172125dfc0717" - } - }, - "acd39276db17439798a97abc56460b0f": { - "model_module": "@jupyter-widgets/controls", "model_name": "HTMLModel", - "model_module_version": "1.5.0", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -7583,40 +3863,120 @@ "_view_name": "HTMLView", "description": "", "description_tooltip": null, - "layout": "IPY_MODEL_d452b32c54e14e41a17fd7d51862ba8e", + "layout": "IPY_MODEL_67f82b82ebb74d0fb3c68b9c8c57d690", "placeholder": "​", - "style": "IPY_MODEL_d1f8f4568a444248b69022d58e3f1af0", - "value": "Batches: 100%" + "style": "IPY_MODEL_b710cb57f19d4490a740c060e8a83b90", + "value": " 350/350 [00:00<00:00, 26.0kB/s]" } }, - "bda474c3b8184597a6a9bc6da0672a50": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", + "1f1cdac013af4559889f15eebac5256a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_0c2e30d78c234b1b8098d879442d3bac", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_9bb8bf12010f42b2b17c10c7ccaa7bf8", - "value": 1 + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "1f1dc0d20cae46feb372203aea6458a0": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null } }, "20a66f9de4ed41c7ac9a8e817898ed9e": { "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", "model_module_version": "1.5.0", + "model_name": "HTMLModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -7634,181 +3994,10 @@ "value": " 1/1 [00:00<00:00, 18.91it/s]" } }, - "e662ba10fbae49d9b66172125dfc0717": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "d452b32c54e14e41a17fd7d51862ba8e": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "d1f8f4568a444248b69022d58e3f1af0": { + "2256ddab0ae1408abb10ba211a08f794": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "0c2e30d78c234b1b8098d879442d3bac": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9bb8bf12010f42b2b17c10c7ccaa7bf8": { - "model_module": "@jupyter-widgets/controls", "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -7821,10 +4010,106 @@ "description_width": "" } }, - "2b2046db907349798e3ae774c15b25d2": { + "228445132e5f4b2ca793f4beeeca4426": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_48a5b775a4324da791603b83d61be7d1", + "max": 612, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_02b60dad91c7482ba70cf8bb954bc4eb", + "value": 612 + } + }, + "24c0be775e474517a7be49d187822bd0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "2563a4677dde47d0a2f7fba5c5dde358": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "2574b07e4af24715aa89d048cc84e358": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_7551b282ef3a4387a801637de2d5c76e", + "placeholder": "​", + "style": "IPY_MODEL_69e5263c812c4542a9e5c31fefaa37fe", + "value": " 1/1 [00:00<00:00, 15.08it/s]" + } + }, + "25821e7aef4e481bbdf3b4698ce3c277": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_7daef1502e2a4140ac021b3b3a6aa12d", + "placeholder": "​", + "style": "IPY_MODEL_1307ef0325bb433d8a1bcc653c7fb291", + "value": " 466k/466k [00:00<00:00, 2.16MB/s]" + } + }, + "269b1ad9dc7b4ebb94d7364c75f3f324": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -7873,25 +4158,291 @@ "width": null } }, - "3c18f449359f422f950543bd976fe323": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", + "288c9da81b3c4d80a4959753da973f58": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", "_view_count": null, "_view_module": "@jupyter-widgets/base", "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null } }, - "472b1acc4c5a4c48b2ec62be42d1830c": { + "29683ef34d5646c687118a2a0cdec6d4": { "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8d370762fafd4d7887ff68ea8279d083", + "placeholder": "​", + "style": "IPY_MODEL_b6a0eb553b024a71b737ff47ca8f7633", + "value": " 1/1 [00:01<00:00,  1.24s/it]" + } + }, + "29badfc2eb0345d38d7cfc6c7f8bb1a8": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2b2046db907349798e3ae774c15b25d2": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2bfb0fb5506d4285918a9c94af9ab5d1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2e27a025a416434f8ab3b63049626d11": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "2eff72cbd9bb4f1ca77213602caa9417": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -7903,17 +4454,614 @@ "_view_name": "HBoxView", "box_style": "", "children": [ - "IPY_MODEL_44e34588d6854737b0fb14b4b6a62a95", - "IPY_MODEL_03402ad03418435ca7a550e3246cd300", - "IPY_MODEL_811f115733b14ab4b242a8b11526016c" + "IPY_MODEL_e82b5196209f4b9f919c7abb402a4504", + "IPY_MODEL_fe34706489c14253a5015ff6332ec4e0", + "IPY_MODEL_2574b07e4af24715aa89d048cc84e358" ], - "layout": "IPY_MODEL_e61fdef1dc4b4d809168c0b441b0e6ac" + "layout": "IPY_MODEL_10bc8be68b5545fd8609824b02499ebf" + } + }, + "3015bc3ce98a4221a9dd3be92481435d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "309ea9620a674088a5207206d9a52d54": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_4d7b0983b97f48b2a333d5b2a4ec50a8", + "max": 350, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_e834a64e49534c3586cb77f4ec5eab2d", + "value": 350 + } + }, + "31ab98e0e375416b83b36a98d4958f57": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_90c2e0e012a94521b9f5cb24924771d8", + "placeholder": "​", + "style": "IPY_MODEL_2563a4677dde47d0a2f7fba5c5dde358", + "value": " 90.9M/90.9M [00:00<00:00, 223MB/s]" + } + }, + "35e10db3906248ffa8ab955d2f53bd75": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1f1dc0d20cae46feb372203aea6458a0", + "placeholder": "​", + "style": "IPY_MODEL_43feace0290a47c0b06c3a1c08cc70a9", + "value": "tokenizer.json: 100%" + } + }, + "366add01dc734455a384460c97491215": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0f3bbf28fbed4e97b660bbf3c66a214a", + "max": 190, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_a4b2220ed47f4f85b3f991c92de98964", + "value": 190 + } + }, + "38a958036c6e4155815a8169f1be1e53": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3a46a46bc8124a92b27aef43cbc009b6": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3a649adc22694036b35bab04ff03d338": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "3ac596104cdc4439b3980f7ce66ad080": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_40e9f20d74374b0e82c653caa0559d04", + "max": 53, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_f46cfc9237e64db6be2ec6529b61ec88", + "value": 53 + } + }, + "3c18f449359f422f950543bd976fe323": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "3c868641db934c67a44e1d26e1a17756": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_a72d01788b484bbeb4375aac3ceadf34", + "IPY_MODEL_366add01dc734455a384460c97491215", + "IPY_MODEL_70accb92e645435b8f1e0c48538f7473" + ], + "layout": "IPY_MODEL_628848757fcf443e806a8f25013cc2b5" + } + }, + "3da95c8814f34472a181ce7687f9e15e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_53a46fe254924e78876db6dd2e1b7123", + "placeholder": "​", + "style": "IPY_MODEL_f2ce01983f0a4f12b318e6d29f1dd4a1", + "value": "model.safetensors: 100%" + } + }, + "3ded85d9c34246e88f8ce693eb8025e5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3ec694106303491ea112a257309bc69c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4004cda1d84949f5a380536f8a9d0274": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "40e9f20d74374b0e82c653caa0559d04": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "42335bcbc6ee40a79d36c5159cc7da06": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4282ee7d947e426ba863df9970e82f3f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "43feace0290a47c0b06c3a1c08cc70a9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" } }, "44e34588d6854737b0fb14b4b6a62a95": { "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", "model_module_version": "1.5.0", + "model_name": "HTMLModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -7931,174 +5079,10 @@ "value": "Batches: 100%" } }, - "03402ad03418435ca7a550e3246cd300": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_9df914248c214597bed7d7980c7a0afe", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_4709067f3f554b93b3ef35e3f58cbf85", - "value": 1 - } - }, - "811f115733b14ab4b242a8b11526016c": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_02baf670942347d69c290452de8641e4", - "placeholder": "​", - "style": "IPY_MODEL_7611cfc7965649ba88ca57c1a9f9ccf3", - "value": " 1/1 [00:00<00:00, 13.00it/s]" - } - }, - "e61fdef1dc4b4d809168c0b441b0e6ac": { + "45aadb26b382460eb5b6b147509fb75a": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "631c9a95127244c79875c829a7637df6": { - "model_module": "@jupyter-widgets/base", "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "d25492ad867141bfa8d957d2464b8639": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "9df914248c214597bed7d7980c7a0afe": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -8149,8 +5133,8 @@ }, "4709067f3f554b93b3ef35e3f58cbf85": { "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -8163,10 +5147,856 @@ "description_width": "" } }, - "02baf670942347d69c290452de8641e4": { + "472b1acc4c5a4c48b2ec62be42d1830c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_44e34588d6854737b0fb14b4b6a62a95", + "IPY_MODEL_03402ad03418435ca7a550e3246cd300", + "IPY_MODEL_811f115733b14ab4b242a8b11526016c" + ], + "layout": "IPY_MODEL_e61fdef1dc4b4d809168c0b441b0e6ac" + } + }, + "487477e023b64947bf42f83dc6275ef1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_a92a7bce961e4291b126fda3c540636b", + "placeholder": "​", + "style": "IPY_MODEL_01b3e7803d1946118d27acda0c067da2", + "value": " 232k/232k [00:00<00:00, 550kB/s]" + } + }, + "48a5b775a4324da791603b83d61be7d1": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4ad6bc0cca62446d8faf19a341bfa86f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "4b83e3caa8ec47169dca04ee9599adeb": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_269b1ad9dc7b4ebb94d7364c75f3f324", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_2256ddab0ae1408abb10ba211a08f794", + "value": 1 + } + }, + "4d1c2de4c1354ef0b84c54c447141707": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1b7af9f7204547b8b4a718a780af0ded", + "max": 90868376, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_a4bb5a59d1324585b0a34c9bb2820b7f", + "value": 90868376 + } + }, + "4d7b0983b97f48b2a333d5b2a4ec50a8": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5023c2b8cf9846069d116237826fed7f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_960c2f44166b4ac7910af6512832186f", + "IPY_MODEL_309ea9620a674088a5207206d9a52d54", + "IPY_MODEL_1c86d856083c4ef99976849c7a1c9100" + ], + "layout": "IPY_MODEL_5d9bf2102da143c1b9e1483e05add4e5" + } + }, + "509863a58de74b07b813aa83ffa4a507": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "52f1d69c6cd04816b6f34657893ae32b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "53a46fe254924e78876db6dd2e1b7123": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "542aa4a847cf4a66a4b3fc93c241363b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "54bddcf41c5641b7a56c981aadb62ef1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5a620017a5384af1a056de687b2670db": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "5c9ec25994914acd8e13866b3eb943e1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_dc04575da46540d4ad3a708e58f0de6a", + "placeholder": "​", + "style": "IPY_MODEL_24c0be775e474517a7be49d187822bd0", + "value": " 53.0/53.0 [00:00<00:00, 3.84kB/s]" + } + }, + "5d9bf2102da143c1b9e1483e05add4e5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5effefa8e3764e3aaff57fe0197a7c96": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "628848757fcf443e806a8f25013cc2b5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "631c9a95127244c79875c829a7637df6": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "6437c99289f947449f7d2964288973e5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "67f82b82ebb74d0fb3c68b9c8c57d690": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "69e5263c812c4542a9e5c31fefaa37fe": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "70accb92e645435b8f1e0c48538f7473": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_b6a505e6c863409db1b906423f99125a", + "placeholder": "​", + "style": "IPY_MODEL_d9560d20106a42ec904e7e315f99ff01", + "value": " 190/190 [00:00<00:00, 9.18kB/s]" + } + }, + "713c09d1275a43b0af7c2ae8e126517f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_b62fe08114f549ea99808e8df95c7cad", + "IPY_MODEL_af722d177320422e97c679b24cb754f6", + "IPY_MODEL_487477e023b64947bf42f83dc6275ef1" + ], + "layout": "IPY_MODEL_bcf0d3af3bc0439e97023937852941e9" + } + }, + "7363b1a9a1b54a57bf15357e897128fd": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_cf5113a647ce45c4a3a523361aa3b5af", + "placeholder": "​", + "style": "IPY_MODEL_da8c20a65ba541bda058614849d5cfe2", + "value": "sentence_bert_config.json: 100%" + } + }, + "7551b282ef3a4387a801637de2d5c76e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -8217,8 +6047,8 @@ }, "7611cfc7965649ba88ca57c1a9f9ccf3": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -8230,10 +6060,25 @@ "description_width": "" } }, - "15ae23892b634a9f821a8fcee14e500b": { + "79b9fb75dc1d486c9fc881a90b6f1060": { "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "7cc356ed20e94401b72a0e138ad0f5df": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -8245,17 +6090,1178 @@ "_view_name": "HBoxView", "box_style": "", "children": [ - "IPY_MODEL_b28d46c2ecdd46b9b3f2da871afbf1cb", - "IPY_MODEL_4b83e3caa8ec47169dca04ee9599adeb", - "IPY_MODEL_c83c23161674484e81f0db9856c23eb6" + "IPY_MODEL_acd39276db17439798a97abc56460b0f", + "IPY_MODEL_bda474c3b8184597a6a9bc6da0672a50", + "IPY_MODEL_20a66f9de4ed41c7ac9a8e817898ed9e" ], - "layout": "IPY_MODEL_3ded85d9c34246e88f8ce693eb8025e5" + "layout": "IPY_MODEL_e662ba10fbae49d9b66172125dfc0717" + } + }, + "7daef1502e2a4140ac021b3b3a6aa12d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "80e884cae6ea42eaa37f028120963355": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_9f185162847f4cb2828af81c92116582", + "max": 466247, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_3a649adc22694036b35bab04ff03d338", + "value": 466247 + } + }, + "811f115733b14ab4b242a8b11526016c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_02baf670942347d69c290452de8641e4", + "placeholder": "​", + "style": "IPY_MODEL_7611cfc7965649ba88ca57c1a9f9ccf3", + "value": " 1/1 [00:00<00:00, 13.00it/s]" + } + }, + "834ae2d249b94be6bbe5349509536a4b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "85569eaf3ae3488b808131cd460f6514": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "88f0c88612bb45d59f07e93567cc0e14": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_9b24a82117e1482a8f6665978e84089c", + "IPY_MODEL_8e75bf7cac454eeabd5ce47a1e981c68", + "IPY_MODEL_fc272883566541108f83117ccd146a21" + ], + "layout": "IPY_MODEL_2e27a025a416434f8ab3b63049626d11" + } + }, + "895efd0b6d9f4b319159703d965d1966": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_dece6dff65394a5f93585c73359d4dad", + "IPY_MODEL_1030c0848635497681cc9ff0c344fb1a", + "IPY_MODEL_fa6ecaab432347de8427b9b5ac3d4524" + ], + "layout": "IPY_MODEL_5effefa8e3764e3aaff57fe0197a7c96" + } + }, + "8ab411217bfd486ca3fb8b885fff4690": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8b9ebe06b4e045a29269128ec97d9f62": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8c0d69b735c94b719160d39256c643cc": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "8d370762fafd4d7887ff68ea8279d083": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8de1cba3a7c0422eb2a21e3f8b2059c7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8dee873065a047799a04e49ab791e449": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ec747bd7c37c45298896c513634cd59a", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_5a620017a5384af1a056de687b2670db", + "value": 1 + } + }, + "8e75bf7cac454eeabd5ce47a1e981c68": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_6437c99289f947449f7d2964288973e5", + "max": 349, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_e2f7dea8fc744537b42d0f1a85a73eb4", + "value": 349 + } + }, + "90c2e0e012a94521b9f5cb24924771d8": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "916190b4615e4c5c9f3e55c0804a3502": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "91e103573c034ceda689047c61294b17": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "95db8eab3f964edf99038ad53f41fabc": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "960c2f44166b4ac7910af6512832186f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_85569eaf3ae3488b808131cd460f6514", + "placeholder": "​", + "style": "IPY_MODEL_3015bc3ce98a4221a9dd3be92481435d", + "value": "tokenizer_config.json: 100%" + } + }, + "980292182c7144e194604c13ac544a26": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_288c9da81b3c4d80a4959753da973f58", + "placeholder": "​", + "style": "IPY_MODEL_cf453a1ed54645aba656f9a3f1461e69", + "value": "Batches: 100%" + } + }, + "9b11eaf2d50a447384b75eb7f73829eb": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "9b24a82117e1482a8f6665978e84089c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_3a46a46bc8124a92b27aef43cbc009b6", + "placeholder": "​", + "style": "IPY_MODEL_4ad6bc0cca62446d8faf19a341bfa86f", + "value": "modules.json: 100%" + } + }, + "9bb8bf12010f42b2b17c10c7ccaa7bf8": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "9df914248c214597bed7d7980c7a0afe": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9ee45247ec144bb3aafe4208f316063f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_da330e0999cb4c3c91a1cb1026304568", + "IPY_MODEL_ff58a5381fb74cb1b9efc10f5c2738d6", + "IPY_MODEL_18ed62b1d4594ed9a2651fa5df046efc" + ], + "layout": "IPY_MODEL_4004cda1d84949f5a380536f8a9d0274" + } + }, + "9f185162847f4cb2828af81c92116582": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a0639d5360044f97ac5b9374c735ff4b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a4b2220ed47f4f85b3f991c92de98964": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "a4bb5a59d1324585b0a34c9bb2820b7f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "a72d01788b484bbeb4375aac3ceadf34": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ebf411690c844daf89b87c120e3cb67e", + "placeholder": "​", + "style": "IPY_MODEL_79b9fb75dc1d486c9fc881a90b6f1060", + "value": "1_Pooling/config.json: 100%" + } + }, + "a92a7bce961e4291b126fda3c540636b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a9a0d8415d9d4e98a3f02ae8ec1053da": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "acd39276db17439798a97abc56460b0f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d452b32c54e14e41a17fd7d51862ba8e", + "placeholder": "​", + "style": "IPY_MODEL_d1f8f4568a444248b69022d58e3f1af0", + "value": "Batches: 100%" + } + }, + "af722d177320422e97c679b24cb754f6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_91e103573c034ceda689047c61294b17", + "max": 231508, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_b9eac61fb55342f4bf9834f321899836", + "value": 231508 } }, "b28d46c2ecdd46b9b3f2da871afbf1cb": { "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", "model_module_version": "1.5.0", + "model_name": "HTMLModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -8273,10 +7279,135 @@ "value": "Batches: 100%" } }, - "4b83e3caa8ec47169dca04ee9599adeb": { + "b62fe08114f549ea99808e8df95c7cad": { "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d83a1e1e678e4efd83115f9aee0ffc8d", + "placeholder": "​", + "style": "IPY_MODEL_f210583576594e759387fc704695ad09", + "value": "vocab.txt: 100%" + } + }, + "b6a0eb553b024a71b737ff47ca8f7633": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "b6a505e6c863409db1b906423f99125a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b710cb57f19d4490a740c060e8a83b90": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "b79a1dfcf2904bcba332569dbf351f34": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_7363b1a9a1b54a57bf15357e897128fd", + "IPY_MODEL_3ac596104cdc4439b3980f7ce66ad080", + "IPY_MODEL_5c9ec25994914acd8e13866b3eb943e1" + ], + "layout": "IPY_MODEL_38a958036c6e4155815a8169f1be1e53" + } + }, + "b7f9a3c97f2043f380bdc1827961c649": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -8289,18 +7420,190 @@ "bar_style": "success", "description": "", "description_tooltip": null, - "layout": "IPY_MODEL_269b1ad9dc7b4ebb94d7364c75f3f324", + "layout": "IPY_MODEL_8ab411217bfd486ca3fb8b885fff4690", + "max": 112, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_c80ea8c54211427087712b5500e26edf", + "value": 112 + } + }, + "b96a2e34a2af435b9705550fe564591d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_2bfb0fb5506d4285918a9c94af9ab5d1", + "placeholder": "​", + "style": "IPY_MODEL_0f699b0f99484a8ba2eb17bb1d621c5a", + "value": " 612/612 [00:00<00:00, 47.5kB/s]" + } + }, + "b9eac61fb55342f4bf9834f321899836": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "bcf0d3af3bc0439e97023937852941e9": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "bda474c3b8184597a6a9bc6da0672a50": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0c2e30d78c234b1b8098d879442d3bac", "max": 1, "min": 0, "orientation": "horizontal", - "style": "IPY_MODEL_2256ddab0ae1408abb10ba211a08f794", + "style": "IPY_MODEL_9bb8bf12010f42b2b17c10c7ccaa7bf8", "value": 1 } }, + "be060f9d7a664c17a80510f447c0bee3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_834ae2d249b94be6bbe5349509536a4b", + "placeholder": "​", + "style": "IPY_MODEL_509863a58de74b07b813aa83ffa4a507", + "value": "config.json: 100%" + } + }, + "c6f34317390e4f90b16235f2ae84a981": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_3da95c8814f34472a181ce7687f9e15e", + "IPY_MODEL_4d1c2de4c1354ef0b84c54c447141707", + "IPY_MODEL_31ab98e0e375416b83b36a98d4958f57" + ], + "layout": "IPY_MODEL_8b9ebe06b4e045a29269128ec97d9f62" + } + }, + "c80ea8c54211427087712b5500e26edf": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, "c83c23161674484e81f0db9856c23eb6": { "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", "model_module_version": "1.5.0", + "model_name": "HTMLModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -8318,10 +7621,10 @@ "value": " 1/1 [00:00<00:00, 14.00it/s]" } }, - "3ded85d9c34246e88f8ce693eb8025e5": { + "cceff1126242494bab432205c7ac7345": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -8370,62 +7673,10 @@ "width": null } }, - "0ac8e976a32c4f5989392b8088546e00": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "ed4b0035752546cc81688a7a77ba27c0": { + "cf453a1ed54645aba656f9a3f1461e69": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -8437,78 +7688,10 @@ "description_width": "" } }, - "269b1ad9dc7b4ebb94d7364c75f3f324": { + "cf5113a647ce45c4a3a523361aa3b5af": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2256ddab0ae1408abb10ba211a08f794": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "42335bcbc6ee40a79d36c5159cc7da06": { - "model_module": "@jupyter-widgets/base", "model_name": "LayoutModel", - "model_module_version": "1.2.0", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -8559,8 +7742,8 @@ }, "cf694e1b797246b096ae588973dc985f": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -8571,6 +7754,899 @@ "_view_name": "StyleView", "description_width": "" } + }, + "cfe6be8fd8254bc084a81b1d06e86ae1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d1f8f4568a444248b69022d58e3f1af0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "d2473b7a6c5b4483981516af2fc59bde": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d25492ad867141bfa8d957d2464b8639": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "d452b32c54e14e41a17fd7d51862ba8e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d83a1e1e678e4efd83115f9aee0ffc8d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d9560d20106a42ec904e7e315f99ff01": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "da330e0999cb4c3c91a1cb1026304568": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_54bddcf41c5641b7a56c981aadb62ef1", + "placeholder": "​", + "style": "IPY_MODEL_a9a0d8415d9d4e98a3f02ae8ec1053da", + "value": "README.md: 100%" + } + }, + "da8c20a65ba541bda058614849d5cfe2": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "dc04575da46540d4ad3a708e58f0de6a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "dece6dff65394a5f93585c73359d4dad": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1756eceba2c34c1ca182b7db465e95ce", + "placeholder": "​", + "style": "IPY_MODEL_0fd62e56e0bb41a996c04e63381d2a29", + "value": "config_sentence_transformers.json: 100%" + } + }, + "e2f7dea8fc744537b42d0f1a85a73eb4": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "e61fdef1dc4b4d809168c0b441b0e6ac": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e64cedb4560a43d8a43f36002087ac30": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "e662ba10fbae49d9b66172125dfc0717": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e6e53c439dab4639adc1c3c873602476": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "e82b5196209f4b9f919c7abb402a4504": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d2473b7a6c5b4483981516af2fc59bde", + "placeholder": "​", + "style": "IPY_MODEL_4282ee7d947e426ba863df9970e82f3f", + "value": "Batches: 100%" + } + }, + "e834a64e49534c3586cb77f4ec5eab2d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "ebf411690c844daf89b87c120e3cb67e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ec747bd7c37c45298896c513634cd59a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ed4b0035752546cc81688a7a77ba27c0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "edc4d84302f746d39a43e8107af6b67b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_980292182c7144e194604c13ac544a26", + "IPY_MODEL_8dee873065a047799a04e49ab791e449", + "IPY_MODEL_29683ef34d5646c687118a2a0cdec6d4" + ], + "layout": "IPY_MODEL_3ec694106303491ea112a257309bc69c" + } + }, + "f01d7a1404a943a08c84adce14a262c7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_f15cdedf8e7b4a44993644a5ff070e78", + "IPY_MODEL_b7f9a3c97f2043f380bdc1827961c649", + "IPY_MODEL_0b64892a98d14a3b85b128df77d8e7d6" + ], + "layout": "IPY_MODEL_8de1cba3a7c0422eb2a21e3f8b2059c7" + } + }, + "f097b32928f246de9b01fea6f9b092f7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_35e10db3906248ffa8ab955d2f53bd75", + "IPY_MODEL_80e884cae6ea42eaa37f028120963355", + "IPY_MODEL_25821e7aef4e481bbdf3b4698ce3c277" + ], + "layout": "IPY_MODEL_916190b4615e4c5c9f3e55c0804a3502" + } + }, + "f15cdedf8e7b4a44993644a5ff070e78": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_a0639d5360044f97ac5b9374c735ff4b", + "placeholder": "​", + "style": "IPY_MODEL_9b11eaf2d50a447384b75eb7f73829eb", + "value": "special_tokens_map.json: 100%" + } + }, + "f210583576594e759387fc704695ad09": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "f2ce01983f0a4f12b318e6d29f1dd4a1": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "f46cfc9237e64db6be2ec6529b61ec88": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "fa6ecaab432347de8427b9b5ac3d4524": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_45aadb26b382460eb5b6b147509fb75a", + "placeholder": "​", + "style": "IPY_MODEL_130f2f5840764e8dbd573cc8a6ea6f5f", + "value": " 116/116 [00:00<00:00, 3.35kB/s]" + } + }, + "fc272883566541108f83117ccd146a21": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1377d2160344430da8f29a50d113a288", + "placeholder": "​", + "style": "IPY_MODEL_0c0b30e126724f9282ac5acbcb4581db", + "value": " 349/349 [00:00<00:00, 7.72kB/s]" + } + }, + "fe34706489c14253a5015ff6332ec4e0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_cfe6be8fd8254bc084a81b1d06e86ae1", + "max": 1, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_1817f6732a5f44c7adc75a644b1acef2", + "value": 1 + } + }, + "ff58a5381fb74cb1b9efc10f5c2738d6": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_cceff1126242494bab432205c7ac7345", + "max": 10659, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_e6e53c439dab4639adc1c3c873602476", + "value": 10659 + } } } } From 05f6b44da79ddd45b40e323ede34b86e700737bc Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 16 Jan 2025 10:36:13 -0800 Subject: [PATCH 025/107] Fix telemetry (#787) # What does this PR do? PR fixes couple of issues with telemetry: 1) The REST refactor changed the method from get_span_tree to query_span_tree, which is causing the server side to return empty spans 2) Library client has introduced a new event loop, which required changing the location of where start and end trace are called ## Test Plan LLAMA_STACK_CONFIG="/Users/dineshyv/.llama/distributions/llamastack-fireworks/fireworks-run.yaml" pytest -v tests/client-sdk/agents/test_agents.py -k "test_builtin_tool_web_search" And querying for spans from the agent run using the library client. --- llama_stack/distribution/library_client.py | 21 ++++++++++++------- .../utils/telemetry/dataset_mixin.py | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/llama_stack/distribution/library_client.py b/llama_stack/distribution/library_client.py index fdc68c0a4..192667f2c 100644 --- a/llama_stack/distribution/library_client.py +++ b/llama_stack/distribution/library_client.py @@ -269,7 +269,7 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): set_request_provider_data( {"X-LlamaStack-Provider-Data": json.dumps(self.provider_data)} ) - await start_trace(options.url, {"__location__": "library_client"}) + if stream: response = await self._call_streaming( cast_to=cast_to, @@ -281,7 +281,6 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): cast_to=cast_to, options=options, ) - await end_trace() return response def _find_matching_endpoint(self, method: str, path: str) -> tuple[Any, dict]: @@ -323,7 +322,11 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): matched_func, path_params = self._find_matching_endpoint(options.method, path) body |= path_params body = self._convert_body(path, options.method, body) - result = await matched_func(**body) + await start_trace(options.url, {"__location__": "library_client"}) + try: + result = await matched_func(**body) + finally: + await end_trace() json_content = json.dumps(convert_pydantic_to_json_value(result)) mock_response = httpx.Response( @@ -366,10 +369,14 @@ class AsyncLlamaStackAsLibraryClient(AsyncLlamaStackClient): body = self._convert_body(path, options.method, body) async def gen(): - async for chunk in await func(**body): - data = json.dumps(convert_pydantic_to_json_value(chunk)) - sse_event = f"data: {data}\n\n" - yield sse_event.encode("utf-8") + await start_trace(options.url, {"__location__": "library_client"}) + try: + async for chunk in await func(**body): + data = json.dumps(convert_pydantic_to_json_value(chunk)) + sse_event = f"data: {data}\n\n" + yield sse_event.encode("utf-8") + finally: + await end_trace() mock_response = httpx.Response( status_code=httpx.codes.OK, diff --git a/llama_stack/providers/utils/telemetry/dataset_mixin.py b/llama_stack/providers/utils/telemetry/dataset_mixin.py index bf5e79c3d..e488f2475 100644 --- a/llama_stack/providers/utils/telemetry/dataset_mixin.py +++ b/llama_stack/providers/utils/telemetry/dataset_mixin.py @@ -53,7 +53,7 @@ class TelemetryDatasetMixin: spans = [] for trace in traces: - spans_by_id = await self.get_span_tree( + spans_by_id = await self.query_span_tree( span_id=trace.root_span_id, attributes_to_return=attributes_to_return, max_depth=max_depth, From e23928093233c46fab071f4445bf59487c05cd43 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Thu, 16 Jan 2025 10:37:07 -0800 Subject: [PATCH 026/107] fireworks add completion logprobs adapter (#778) # What does this PR do? - add completion log probs for fireworks ## Test Plan image ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- .../remote/inference/fireworks/fireworks.py | 14 ++++++++-- .../utils/inference/openai_compat.py | 26 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/llama_stack/providers/remote/inference/fireworks/fireworks.py b/llama_stack/providers/remote/inference/fireworks/fireworks.py index b451f0264..e22144326 100644 --- a/llama_stack/providers/remote/inference/fireworks/fireworks.py +++ b/llama_stack/providers/remote/inference/fireworks/fireworks.py @@ -168,7 +168,10 @@ class FireworksInferenceAdapter( yield chunk def _build_options( - self, sampling_params: Optional[SamplingParams], fmt: ResponseFormat + self, + sampling_params: Optional[SamplingParams], + fmt: ResponseFormat, + logprobs: Optional[LogProbConfig], ) -> dict: options = get_sampling_options(sampling_params) options.setdefault("max_tokens", 512) @@ -187,6 +190,11 @@ class FireworksInferenceAdapter( else: raise ValueError(f"Unknown response format {fmt.type}") + if logprobs and logprobs.top_k: + options["logprobs"] = logprobs.top_k + if options["logprobs"] <= 0 or options["logprobs"] >= 5: + raise ValueError("Required range: 0 < top_k < 5") + return options async def chat_completion( @@ -280,7 +288,9 @@ class FireworksInferenceAdapter( "model": request.model, **input_dict, "stream": request.stream, - **self._build_options(request.sampling_params, request.response_format), + **self._build_options( + request.sampling_params, request.response_format, request.logprobs + ), } async def embeddings( diff --git a/llama_stack/providers/utils/inference/openai_compat.py b/llama_stack/providers/utils/inference/openai_compat.py index 694212a02..f6350ed51 100644 --- a/llama_stack/providers/utils/inference/openai_compat.py +++ b/llama_stack/providers/utils/inference/openai_compat.py @@ -4,7 +4,7 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -from typing import AsyncGenerator, List, Optional +from typing import AsyncGenerator, Dict, List, Optional from llama_models.llama3.api.chat_format import ChatFormat @@ -34,6 +34,7 @@ from llama_stack.apis.inference import ( CompletionResponse, CompletionResponseStreamChunk, Message, + TokenLogProbs, ) from llama_stack.providers.utils.inference.prompt_adapter import ( @@ -45,10 +46,21 @@ class OpenAICompatCompletionChoiceDelta(BaseModel): content: str +class OpenAICompatLogprobs(BaseModel): + text_offset: Optional[List[int]] = None + + token_logprobs: Optional[List[float]] = None + + tokens: Optional[List[str]] = None + + top_logprobs: Optional[List[Dict[str, float]]] = None + + class OpenAICompatCompletionChoice(BaseModel): finish_reason: Optional[str] = None text: Optional[str] = None delta: Optional[OpenAICompatCompletionChoiceDelta] = None + logprobs: Optional[OpenAICompatLogprobs] = None class OpenAICompatCompletionResponse(BaseModel): @@ -104,6 +116,14 @@ def get_stop_reason(finish_reason: str) -> StopReason: return StopReason.out_of_tokens +def convert_openai_completion_logprobs( + logprobs: Optional[OpenAICompatLogprobs], +) -> Optional[List[TokenLogProbs]]: + if not logprobs: + return None + return [TokenLogProbs(logprobs_by_token=x) for x in logprobs.top_logprobs] + + def process_completion_response( response: OpenAICompatCompletionResponse, formatter: ChatFormat ) -> CompletionResponse: @@ -113,16 +133,19 @@ def process_completion_response( return CompletionResponse( stop_reason=StopReason.end_of_turn, content=choice.text[: -len("<|eot_id|>")], + logprobs=convert_openai_completion_logprobs(choice.logprobs), ) # drop suffix if present and return stop reason as end of message if choice.text.endswith("<|eom_id|>"): return CompletionResponse( stop_reason=StopReason.end_of_message, content=choice.text[: -len("<|eom_id|>")], + logprobs=convert_openai_completion_logprobs(choice.logprobs), ) return CompletionResponse( stop_reason=get_stop_reason(choice.finish_reason), content=choice.text, + logprobs=convert_openai_completion_logprobs(choice.logprobs), ) @@ -165,6 +188,7 @@ async def process_completion_stream_response( yield CompletionResponseStreamChunk( delta=text, stop_reason=stop_reason, + logprobs=convert_openai_completion_logprobs(choice.logprobs), ) if finish_reason: if finish_reason in ["stop", "eos", "eos_token"]: From 678ab29129cb106d96f1612d1ba270a5aebb1af0 Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 16 Jan 2025 10:39:42 -0800 Subject: [PATCH 027/107] Idiomatic REST API: Inspect (#779) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # What does this PR do? Since provider list returns a map grouping providers by API, we should not be using data. This PR fixes the types to just be the plain dict, basically reverting back to previous behavior ## Test Plan llama-stack on  fix-provider-list [$] 🅒 stack❯ LLAMA_STACK_CONFIG="/Users/dineshyv/.llama/distributions/llamastack-together/together-run.yaml" pytest -v tests/client-sdk/safety/test_safety.py --- llama_stack/apis/inspect/inspect.py | 12 ++++++++---- llama_stack/distribution/inspect.py | 20 +++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/llama_stack/apis/inspect/inspect.py b/llama_stack/apis/inspect/inspect.py index e2bb98217..9d20c27b3 100644 --- a/llama_stack/apis/inspect/inspect.py +++ b/llama_stack/apis/inspect/inspect.py @@ -4,7 +4,7 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -from typing import Dict, List, Protocol, runtime_checkable +from typing import List, Protocol, runtime_checkable from llama_models.schema_utils import json_schema_type, webmethod from pydantic import BaseModel @@ -38,13 +38,17 @@ class ListProvidersResponse(BaseModel): data: List[ProviderInfo] +class ListRoutesResponse(BaseModel): + data: List[RouteInfo] + + @runtime_checkable class Inspect(Protocol): - @webmethod(route="/providers/list", method="GET") + @webmethod(route="/inspect/providers", method="GET") async def list_providers(self) -> ListProvidersResponse: ... - @webmethod(route="/routes/list", method="GET") - async def list_routes(self) -> Dict[str, List[RouteInfo]]: ... + @webmethod(route="/inspect/routes", method="GET") + async def list_routes(self) -> ListRoutesResponse: ... @webmethod(route="/health", method="GET") async def health(self) -> HealthInfo: ... diff --git a/llama_stack/distribution/inspect.py b/llama_stack/distribution/inspect.py index d275a5c2f..08dfb329e 100644 --- a/llama_stack/distribution/inspect.py +++ b/llama_stack/distribution/inspect.py @@ -5,13 +5,14 @@ # the root directory of this source tree. from importlib.metadata import version -from typing import Dict, List from pydantic import BaseModel from llama_stack.apis.inspect import ( HealthInfo, Inspect, + ListProvidersResponse, + ListRoutesResponse, ProviderInfo, RouteInfo, VersionInfo, @@ -38,36 +39,37 @@ class DistributionInspectImpl(Inspect): async def initialize(self) -> None: pass - async def list_providers(self) -> Dict[str, List[ProviderInfo]]: + async def list_providers(self) -> ListProvidersResponse: run_config = self.config.run_config - ret = {} + ret = [] for api, providers in run_config.providers.items(): - ret[api] = [ + ret.append( ProviderInfo( provider_id=p.provider_id, provider_type=p.provider_type, ) for p in providers - ] + ) return ret - async def list_routes(self) -> Dict[str, List[RouteInfo]]: + async def list_routes(self) -> ListRoutesResponse: run_config = self.config.run_config - ret = {} + ret = [] all_endpoints = get_all_api_endpoints() for api, endpoints in all_endpoints.items(): providers = run_config.providers.get(api.value, []) - ret[api.value] = [ + ret.append( RouteInfo( route=e.route, method=e.method, provider_types=[p.provider_type for p in providers], ) for e in endpoints - ] + ) + return ret async def health(self) -> HealthInfo: From 8d30ecb91adc59d36d325b4867e5ad83d73a6129 Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 16 Jan 2025 11:02:42 -0800 Subject: [PATCH 028/107] Idiomatic REST API: Evals (#782) # What does this PR do? Changes Evals API to follow more idiomatic REST ## Test Plan TBD, once i get an approval for rest endpoints --- llama_stack/apis/eval/eval.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/llama_stack/apis/eval/eval.py b/llama_stack/apis/eval/eval.py index 1b8f768ad..c9d2fb70b 100644 --- a/llama_stack/apis/eval/eval.py +++ b/llama_stack/apis/eval/eval.py @@ -74,14 +74,14 @@ class EvaluateResponse(BaseModel): class Eval(Protocol): - @webmethod(route="/eval/run", method="POST") + @webmethod(route="/eval/tasks/{task_id}/jobs", method="POST") async def run_eval( self, task_id: str, task_config: EvalTaskConfig, ) -> Job: ... - @webmethod(route="/eval/evaluate-rows", method="POST") + @webmethod(route="/eval/tasks/{task_id}/evaluations", method="POST") async def evaluate_rows( self, task_id: str, @@ -90,11 +90,11 @@ class Eval(Protocol): task_config: EvalTaskConfig, ) -> EvaluateResponse: ... - @webmethod(route="/eval/jobs/{job_id}", method="GET") - async def job_status(self, job_id: str, task_id: str) -> Optional[JobStatus]: ... + @webmethod(route="/eval/tasks/{task_id}/jobs/{job_id}", method="GET") + async def job_status(self, task_id: str, job_id: str) -> Optional[JobStatus]: ... - @webmethod(route="/eval/jobs/cancel", method="POST") - async def job_cancel(self, job_id: str, task_id: str) -> None: ... + @webmethod(route="/eval/tasks/{task_id}/jobs/{job_id}", method="DELETE") + async def job_cancel(self, task_id: str, job_id: str) -> None: ... - @webmethod(route="/eval/jobs/{job_id}/result", method="GET") + @webmethod(route="/eval/tasks/{task_id}/jobs/{job_id}/result", method="GET") async def job_result(self, job_id: str, task_id: str) -> EvaluateResponse: ... From 821ac674ab156d99020ed29a570cbf1c1166e96b Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Thu, 16 Jan 2025 11:24:50 -0800 Subject: [PATCH 029/107] Add notebook testing to nightly build job (#785) # What does this PR do? Adds testing of the notebook to the nightly build job ## Test Plan Here is a sample run -- https://github.com/meta-llama/llama-stack/actions/runs/12815889197?pr=785 --------- Co-authored-by: Hardik Shah --- .github/workflows/publish-to-test-pypi.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml index 957761235..35cbc4dc3 100644 --- a/.github/workflows/publish-to-test-pypi.yml +++ b/.github/workflows/publish-to-test-pypi.yml @@ -199,7 +199,13 @@ jobs: - publish-to-testpypi - trigger-client-and-models-build runs-on: ubuntu-latest + env: + TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }} + TAVILY_SEARCH_API_KEY: ${{ secrets.TAVILY_SEARCH_API_KEY }} steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false - name: Install the package run: | max_attempts=6 @@ -228,5 +234,10 @@ jobs: llama stack list-apis llama stack list-providers inference llama stack list-providers telemetry + - name: Test Notebook + run: | + pip install pytest nbval + llama stack build --template together --image-type venv + pytest -v -s --nbval-lax ./docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb # TODO: add trigger for integration test workflow & docker builds From 74e4d520ac0213e933b0a8ed37a9112130a31b50 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Thu, 16 Jan 2025 11:54:25 -0800 Subject: [PATCH 030/107] un-skip telemetry cells in notebook --- docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index 97c30a209..d91a7bea3 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -2865,7 +2865,6 @@ } ], "source": [ - "# NBVAL_SKIP\n", "print(f\"Getting traces for session_id={session_id}\")\n", "import json\n", "\n", @@ -2985,8 +2984,6 @@ } ], "source": [ - "# NBVAL_SKIP\n", - "\n", "# post-process telemetry spance and prepare data for eval\n", "# in this case, we want to assert that all user prompts is followed by a tool call\n", "import ast\n", From c79b08755220b0bad2f6c262187a09541318c2a8 Mon Sep 17 00:00:00 2001 From: Sixian Yi Date: Thu, 16 Jan 2025 12:05:49 -0800 Subject: [PATCH 031/107] [test automation] support run tests on config file (#730) # Context For test automation, the end goal is to run a single pytest command from root test directory (llama_stack/providers/tests/.) such that we execute push-blocking tests The work plan: 1) trigger pytest from llama_stack/providers/tests/. 2) use config file to determine what tests and parametrization we want to run # What does this PR do? 1) consolidates the "inference-models" / "embedding-model" / "judge-model" ... options in root conftest.py. Without this change, we will hit into error when trying to run `pytest /Users/sxyi/llama-stack/llama_stack/providers/tests/.` because of duplicated `addoptions` definitions across child conftest files. 2) Add a `config` option to specify test config in YAML. (see [`ci_test_config.yaml`](https://gist.github.com/sixianyi0721/5b37fbce4069139445c2f06f6e42f87e) for example config file) For provider_fixtures, we allow users to use either a default fixture combination or define their own {api:provider} combinations. ``` memory: .... fixtures: provider_fixtures: - default_fixture_param_id: ollama // use default fixture combination with param_id="ollama" in [providers/tests/memory/conftest.py](https://fburl.com/mtjzwsmk) - inference: sentence_transformers memory: faiss - default_fixture_param_id: chroma ``` 3) generate tests according to the config. Logic lives in two places: a) in `{api}/conftest.py::pytest_generate_tests`, we read from config to do parametrization. b) after test collection, in `pytest_collection_modifyitems`, we filter the tests to include only functions listed in config. ## Test Plan 1) `pytest /Users/sxyi/llama-stack/llama_stack/providers/tests/. --collect-only --config=ci_test_config.yaml` Using `--collect-only` tag to print the pytests listed in the config file (`ci_test_config.yaml`). output: [gist](https://gist.github.com/sixianyi0721/05145e60d4d085c17cfb304beeb1e60e) 2) sanity check on `--inference-model` option ``` pytest -v -s -k "ollama" --inference-model="meta-llama/Llama-3.1-8B-Instruct" ./llama_stack/providers/tests/inference/test_text_inference.py ``` ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [x] Ran pre-commit to handle lint / formatting issues. - [x] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- .../distribution/routers/routing_tables.py | 2 +- llama_stack/providers/tests/README.md | 14 ++ .../providers/tests/agents/conftest.py | 39 +++-- .../tests/agents/test_persistence.py | 4 +- .../providers/tests/ci_test_config.yaml | 55 +++++++ llama_stack/providers/tests/conftest.py | 144 +++++++++++++++++- llama_stack/providers/tests/eval/conftest.py | 16 -- .../providers/tests/inference/conftest.py | 48 +++--- .../providers/tests/inference/fixtures.py | 1 + .../providers/tests/memory/conftest.py | 25 +-- .../tests/post_training/test_post_training.py | 2 +- .../providers/tests/safety/conftest.py | 9 -- .../providers/tests/scoring/conftest.py | 15 -- llama_stack/providers/tests/tools/conftest.py | 15 -- 14 files changed, 273 insertions(+), 116 deletions(-) create mode 100644 llama_stack/providers/tests/ci_test_config.yaml diff --git a/llama_stack/distribution/routers/routing_tables.py b/llama_stack/distribution/routers/routing_tables.py index e02606936..889bd4624 100644 --- a/llama_stack/distribution/routers/routing_tables.py +++ b/llama_stack/distribution/routers/routing_tables.py @@ -246,7 +246,7 @@ class ModelsRoutingTable(CommonRoutingTableImpl, Models): provider_id = list(self.impls_by_provider_id.keys())[0] else: raise ValueError( - "No provider specified and multiple providers available. Please specify a provider_id. Available providers: {self.impls_by_provider_id.keys()}" + f"No provider specified and multiple providers available. Please specify a provider_id. Available providers: {self.impls_by_provider_id.keys()}" ) if metadata is None: metadata = {} diff --git a/llama_stack/providers/tests/README.md b/llama_stack/providers/tests/README.md index 4b406b321..e4e94a3fd 100644 --- a/llama_stack/providers/tests/README.md +++ b/llama_stack/providers/tests/README.md @@ -10,6 +10,8 @@ We use `pytest` and all of its dynamism to enable the features needed. Specifica - We use `pytest_configure` to make sure we dynamically add appropriate marks based on the fixtures we make. +- We use `pytest_collection_modifyitems` to filter tests based on the test config (if specified). + ## Common options All tests support a `--providers` option which can be a string of the form `api1=provider_fixture1,api2=provider_fixture2`. So, when testing safety (which need inference and safety APIs) you can use `--providers inference=together,safety=meta_reference` to use these fixtures in concert. @@ -73,3 +75,15 @@ If you wanted to test a remotely hosted stack, you can use `-m remote` as follow pytest -s -m remote llama_stack/providers/tests/agents/test_agents.py \ --env REMOTE_STACK_URL=<...> ``` + +## Test Config +If you want to run a test suite with a custom set of tests and parametrizations, you can define a YAML test config under llama_stack/providers/tests/ folder and pass the filename through `--config` option as follows: + +``` +pytest llama_stack/providers/tests/ --config=ci_test_config.yaml +``` + +### Test config format +Currently, we support test config on inference, agents and memory api tests. + +Example format of test config can be found in ci_test_config.yaml. diff --git a/llama_stack/providers/tests/agents/conftest.py b/llama_stack/providers/tests/agents/conftest.py index ecd05dcf8..4efdfe8b7 100644 --- a/llama_stack/providers/tests/agents/conftest.py +++ b/llama_stack/providers/tests/agents/conftest.py @@ -6,10 +6,15 @@ import pytest -from ..conftest import get_provider_fixture_overrides +from ..conftest import ( + get_provider_fixture_overrides, + get_provider_fixture_overrides_from_test_config, + get_test_config_for_api, +) from ..inference.fixtures import INFERENCE_FIXTURES from ..memory.fixtures import MEMORY_FIXTURES from ..safety.fixtures import SAFETY_FIXTURES, safety_model_from_shield + from ..tools.fixtures import TOOL_RUNTIME_FIXTURES from .fixtures import AGENTS_FIXTURES @@ -81,23 +86,15 @@ def pytest_configure(config): ) -def pytest_addoption(parser): - parser.addoption( - "--inference-model", - action="store", - default="meta-llama/Llama-3.2-3B-Instruct", - help="Specify the inference model to use for testing", - ) - parser.addoption( - "--safety-shield", - action="store", - default="meta-llama/Llama-Guard-3-1B", - help="Specify the safety shield to use for testing", - ) - - def pytest_generate_tests(metafunc): - shield_id = metafunc.config.getoption("--safety-shield") + test_config = get_test_config_for_api(metafunc.config, "agents") + shield_id = getattr( + test_config, "safety_shield", None + ) or metafunc.config.getoption("--safety-shield") + inference_models = getattr(test_config, "inference_models", None) or [ + metafunc.config.getoption("--inference-model") + ] + if "safety_shield" in metafunc.fixturenames: metafunc.parametrize( "safety_shield", @@ -105,8 +102,7 @@ def pytest_generate_tests(metafunc): indirect=True, ) if "inference_model" in metafunc.fixturenames: - inference_model = metafunc.config.getoption("--inference-model") - models = set({inference_model}) + models = set(inference_models) if safety_model := safety_model_from_shield(shield_id): models.add(safety_model) @@ -124,7 +120,10 @@ def pytest_generate_tests(metafunc): "tool_runtime": TOOL_RUNTIME_FIXTURES, } combinations = ( - get_provider_fixture_overrides(metafunc.config, available_fixtures) + get_provider_fixture_overrides_from_test_config( + metafunc.config, "agents", DEFAULT_PROVIDER_COMBINATIONS + ) + or get_provider_fixture_overrides(metafunc.config, available_fixtures) or DEFAULT_PROVIDER_COMBINATIONS ) metafunc.parametrize("agents_stack", combinations, indirect=True) diff --git a/llama_stack/providers/tests/agents/test_persistence.py b/llama_stack/providers/tests/agents/test_persistence.py index 38eb7de55..e6b1470ef 100644 --- a/llama_stack/providers/tests/agents/test_persistence.py +++ b/llama_stack/providers/tests/agents/test_persistence.py @@ -9,7 +9,9 @@ import pytest from llama_stack.apis.agents import AgentConfig, Turn from llama_stack.apis.inference import SamplingParams, UserMessage from llama_stack.providers.datatypes import Api -from llama_stack.providers.utils.kvstore import kvstore_impl, SqliteKVStoreConfig +from llama_stack.providers.utils.kvstore import kvstore_impl +from llama_stack.providers.utils.kvstore.config import SqliteKVStoreConfig + from .fixtures import pick_inference_model from .utils import create_agent_session diff --git a/llama_stack/providers/tests/ci_test_config.yaml b/llama_stack/providers/tests/ci_test_config.yaml new file mode 100644 index 000000000..3edcd38bf --- /dev/null +++ b/llama_stack/providers/tests/ci_test_config.yaml @@ -0,0 +1,55 @@ +inference: + tests: + - inference/test_vision_inference.py::test_vision_chat_completion_streaming + - inference/test_vision_inference.py::test_vision_chat_completion_non_streaming + - inference/test_text_inference.py::test_structured_output + - inference/test_text_inference.py::test_chat_completion_streaming + - inference/test_text_inference.py::test_chat_completion_non_streaming + - inference/test_text_inference.py::test_chat_completion_with_tool_calling + - inference/test_text_inference.py::test_chat_completion_with_tool_calling_streaming + + scenarios: + - provider_fixtures: + inference: ollama + - fixture_combo_id: fireworks + - provider_fixtures: + inference: together + # - inference: tgi + # - inference: vllm_remote + + inference_models: + - meta-llama/Llama-3.1-8B-Instruct + - meta-llama/Llama-3.2-11B-Vision-Instruct + + +agents: + tests: + - agents/test_agents.py::test_agent_turns_with_safety + - agents/test_agents.py::test_rag_agent + + scenarios: + - fixture_combo_id: ollama + - fixture_combo_id: together + - fixture_combo_id: fireworks + + inference_models: + - meta-llama/Llama-3.2-1B-Instruct + + safety_shield: meta-llama/Llama-Guard-3-1B + + +memory: + tests: + - memory/test_memory.py::test_query_documents + + scenarios: + - fixture_combo_id: ollama + - provider_fixtures: + inference: sentence_transformers + memory: faiss + - fixture_combo_id: chroma + + inference_models: + - meta-llama/Llama-3.2-1B-Instruct + + embedding_model: all-MiniLM-L6-v2 diff --git a/llama_stack/providers/tests/conftest.py b/llama_stack/providers/tests/conftest.py index 7408a6375..9530695e1 100644 --- a/llama_stack/providers/tests/conftest.py +++ b/llama_stack/providers/tests/conftest.py @@ -5,12 +5,16 @@ # the root directory of this source tree. import os +from collections import defaultdict + from pathlib import Path from typing import Any, Dict, List, Optional import pytest +import yaml + from dotenv import load_dotenv -from pydantic import BaseModel +from pydantic import BaseModel, Field from termcolor import colored from llama_stack.distribution.datatypes import Provider @@ -24,6 +28,83 @@ class ProviderFixture(BaseModel): provider_data: Optional[Dict[str, Any]] = None +class TestScenario(BaseModel): + # provider fixtures can be either a mark or a dictionary of api -> providers + provider_fixtures: Dict[str, str] = Field(default_factory=dict) + fixture_combo_id: Optional[str] = None + + +class APITestConfig(BaseModel): + scenarios: List[TestScenario] = Field(default_factory=list) + inference_models: List[str] = Field(default_factory=list) + + # test name format should be :: + tests: List[str] = Field(default_factory=list) + + +class MemoryApiTestConfig(APITestConfig): + embedding_model: Optional[str] = Field(default_factory=None) + + +class AgentsApiTestConfig(APITestConfig): + safety_shield: Optional[str] = Field(default_factory=None) + + +class TestConfig(BaseModel): + inference: Optional[APITestConfig] = None + agents: Optional[AgentsApiTestConfig] = None + memory: Optional[MemoryApiTestConfig] = None + + +def get_test_config_from_config_file(metafunc_config): + config_file = metafunc_config.getoption("--config") + if config_file is None: + return None + + config_file_path = Path(__file__).parent / config_file + if not config_file_path.exists(): + raise ValueError( + f"Test config {config_file} was specified but not found. Please make sure it exists in the llama_stack/providers/tests directory." + ) + with open(config_file_path, "r") as config_file: + config = yaml.safe_load(config_file) + return TestConfig(**config) + + +def get_test_config_for_api(metafunc_config, api): + test_config = get_test_config_from_config_file(metafunc_config) + if test_config is None: + return None + return getattr(test_config, api) + + +def get_provider_fixture_overrides_from_test_config( + metafunc_config, api, default_provider_fixture_combinations +): + api_config = get_test_config_for_api(metafunc_config, api) + if api_config is None: + return None + + fixture_combo_ids = set() + custom_provider_fixture_combos = [] + for scenario in api_config.scenarios: + if scenario.fixture_combo_id: + fixture_combo_ids.add(scenario.fixture_combo_id) + else: + custom_provider_fixture_combos.append( + pytest.param( + scenario.provider_fixtures, + id=scenario.provider_fixtures.get("inference") or "", + ) + ) + + if len(fixture_combo_ids) > 0: + for default_fixture in default_provider_fixture_combinations: + if default_fixture.id in fixture_combo_ids: + custom_provider_fixture_combos.append(default_fixture) + return custom_provider_fixture_combos + + def remote_stack_fixture() -> ProviderFixture: if url := os.getenv("REMOTE_STACK_URL", None): config = RemoteProviderConfig.from_url(url) @@ -69,10 +150,39 @@ def pytest_addoption(parser): "Example: --providers inference=ollama,safety=meta-reference" ), ) + parser.addoption( + "--config", + action="store", + help="Set test config file (supported format: YAML), e.g. --config=test_config.yml", + ) """Add custom command line options""" parser.addoption( "--env", action="append", help="Set environment variables, e.g. --env KEY=value" ) + parser.addoption( + "--inference-model", + action="store", + default="meta-llama/Llama-3.2-3B-Instruct", + help="Specify the inference model to use for testing", + ) + parser.addoption( + "--safety-shield", + action="store", + default="meta-llama/Llama-Guard-3-1B", + help="Specify the safety shield to use for testing", + ) + parser.addoption( + "--embedding-model", + action="store", + default=None, + help="Specify the embedding model to use for testing", + ) + parser.addoption( + "--judge-model", + action="store", + default="meta-llama/Llama-3.1-8B-Instruct", + help="Specify the judge model to use for testing", + ) def make_provider_id(providers: Dict[str, str]) -> str: @@ -148,6 +258,38 @@ def pytest_itemcollected(item): item.name = f"{item.name}[{marks}]" +def pytest_collection_modifyitems(session, config, items): + test_config = get_test_config_from_config_file(config) + if test_config is None: + return + + required_tests = defaultdict(set) + for api_test_config in [ + test_config.inference, + test_config.memory, + test_config.agents, + ]: + if api_test_config is None: + continue + for test in api_test_config.tests: + arr = test.split("::") + if len(arr) != 2: + raise ValueError(f"Invalid format for test name {test}") + test_path, func_name = arr + required_tests[Path(__file__).parent / test_path].add(func_name) + + new_items, deselected_items = [], [] + for item in items: + func_name = getattr(item, "originalname", item.name) + if func_name in required_tests[item.fspath]: + new_items.append(item) + continue + deselected_items.append(item) + + items[:] = new_items + config.hook.pytest_deselected(items=deselected_items) + + pytest_plugins = [ "llama_stack.providers.tests.inference.fixtures", "llama_stack.providers.tests.safety.fixtures", diff --git a/llama_stack/providers/tests/eval/conftest.py b/llama_stack/providers/tests/eval/conftest.py index 3d6ef01b2..b7a68965e 100644 --- a/llama_stack/providers/tests/eval/conftest.py +++ b/llama_stack/providers/tests/eval/conftest.py @@ -76,22 +76,6 @@ def pytest_configure(config): ) -def pytest_addoption(parser): - parser.addoption( - "--inference-model", - action="store", - default="meta-llama/Llama-3.2-3B-Instruct", - help="Specify the inference model to use for testing", - ) - - parser.addoption( - "--judge-model", - action="store", - default="meta-llama/Llama-3.1-8B-Instruct", - help="Specify the judge model to use for testing", - ) - - def pytest_generate_tests(metafunc): if "eval_stack" in metafunc.fixturenames: available_fixtures = { diff --git a/llama_stack/providers/tests/inference/conftest.py b/llama_stack/providers/tests/inference/conftest.py index 54ebcd83a..1303a1b35 100644 --- a/llama_stack/providers/tests/inference/conftest.py +++ b/llama_stack/providers/tests/inference/conftest.py @@ -6,26 +6,10 @@ import pytest -from ..conftest import get_provider_fixture_overrides - +from ..conftest import get_provider_fixture_overrides, get_test_config_for_api from .fixtures import INFERENCE_FIXTURES -def pytest_addoption(parser): - parser.addoption( - "--inference-model", - action="store", - default=None, - help="Specify the inference model to use for testing", - ) - parser.addoption( - "--embedding-model", - action="store", - default=None, - help="Specify the embedding model to use for testing", - ) - - def pytest_configure(config): for model in ["llama_8b", "llama_3b", "llama_vision"]: config.addinivalue_line( @@ -58,16 +42,21 @@ VISION_MODEL_PARAMS = [ def pytest_generate_tests(metafunc): + test_config = get_test_config_for_api(metafunc.config, "inference") + if "inference_model" in metafunc.fixturenames: - model = metafunc.config.getoption("--inference-model") - if model: + cls_name = metafunc.cls.__name__ + params = [] + inference_models = getattr(test_config, "inference_models", []) + for model in inference_models: + if ("Vision" in cls_name and "Vision" in model) or ( + "Vision" not in cls_name and "Vision" not in model + ): + params.append(pytest.param(model, id=model)) + + if not params: + model = metafunc.config.getoption("--inference-model") params = [pytest.param(model, id="")] - else: - cls_name = metafunc.cls.__name__ - if "Vision" in cls_name: - params = VISION_MODEL_PARAMS - else: - params = MODEL_PARAMS metafunc.parametrize( "inference_model", @@ -83,4 +72,13 @@ def pytest_generate_tests(metafunc): }, ): fixtures = [stack.values[0]["inference"] for stack in filtered_stacks] + if test_config: + if custom_fixtures := [ + ( + scenario.fixture_combo_id + or scenario.provider_fixtures.get("inference") + ) + for scenario in test_config.scenarios + ]: + fixtures = custom_fixtures metafunc.parametrize("inference_stack", fixtures, indirect=True) diff --git a/llama_stack/providers/tests/inference/fixtures.py b/llama_stack/providers/tests/inference/fixtures.py index b6653b65d..0767e940f 100644 --- a/llama_stack/providers/tests/inference/fixtures.py +++ b/llama_stack/providers/tests/inference/fixtures.py @@ -301,6 +301,7 @@ async def inference_stack(request, inference_model): inference_fixture.provider_data, models=[ ModelInput( + provider_id=inference_fixture.providers[0].provider_id, model_id=inference_model, model_type=model_type, metadata=metadata, diff --git a/llama_stack/providers/tests/memory/conftest.py b/llama_stack/providers/tests/memory/conftest.py index 9b6ba177d..87dec4beb 100644 --- a/llama_stack/providers/tests/memory/conftest.py +++ b/llama_stack/providers/tests/memory/conftest.py @@ -6,7 +6,11 @@ import pytest -from ..conftest import get_provider_fixture_overrides +from ..conftest import ( + get_provider_fixture_overrides, + get_provider_fixture_overrides_from_test_config, + get_test_config_for_api, +) from ..inference.fixtures import INFERENCE_FIXTURES from .fixtures import MEMORY_FIXTURES @@ -56,15 +60,6 @@ DEFAULT_PROVIDER_COMBINATIONS = [ ] -def pytest_addoption(parser): - parser.addoption( - "--embedding-model", - action="store", - default=None, - help="Specify the embedding model to use for testing", - ) - - def pytest_configure(config): for fixture_name in MEMORY_FIXTURES: config.addinivalue_line( @@ -74,8 +69,11 @@ def pytest_configure(config): def pytest_generate_tests(metafunc): + test_config = get_test_config_for_api(metafunc.config, "memory") if "embedding_model" in metafunc.fixturenames: - model = metafunc.config.getoption("--embedding-model") + model = getattr(test_config, "embedding_model", None) + # Fall back to the default if not specified by the config file + model = model or metafunc.config.getoption("--embedding-model") if model: params = [pytest.param(model, id="")] else: @@ -89,7 +87,10 @@ def pytest_generate_tests(metafunc): "memory": MEMORY_FIXTURES, } combinations = ( - get_provider_fixture_overrides(metafunc.config, available_fixtures) + get_provider_fixture_overrides_from_test_config( + metafunc.config, "memory", DEFAULT_PROVIDER_COMBINATIONS + ) + or get_provider_fixture_overrides(metafunc.config, available_fixtures) or DEFAULT_PROVIDER_COMBINATIONS ) metafunc.parametrize("memory_stack", combinations, indirect=True) diff --git a/llama_stack/providers/tests/post_training/test_post_training.py b/llama_stack/providers/tests/post_training/test_post_training.py index 0645cd555..0c58c1fa0 100644 --- a/llama_stack/providers/tests/post_training/test_post_training.py +++ b/llama_stack/providers/tests/post_training/test_post_training.py @@ -5,7 +5,7 @@ # the root directory of this source tree. import pytest -from llama_stack.apis.common.type_system import JobStatus +from llama_stack.apis.common.job_types import JobStatus from llama_stack.apis.post_training import ( Checkpoint, DataConfig, diff --git a/llama_stack/providers/tests/safety/conftest.py b/llama_stack/providers/tests/safety/conftest.py index 6846517e3..a5e77f570 100644 --- a/llama_stack/providers/tests/safety/conftest.py +++ b/llama_stack/providers/tests/safety/conftest.py @@ -64,15 +64,6 @@ def pytest_configure(config): ) -def pytest_addoption(parser): - parser.addoption( - "--safety-shield", - action="store", - default=None, - help="Specify the safety shield to use for testing", - ) - - SAFETY_SHIELD_PARAMS = [ pytest.param( "meta-llama/Llama-Guard-3-1B", marks=pytest.mark.guard_1b, id="guard_1b" diff --git a/llama_stack/providers/tests/scoring/conftest.py b/llama_stack/providers/tests/scoring/conftest.py index dc4979dd7..0b4e7d46e 100644 --- a/llama_stack/providers/tests/scoring/conftest.py +++ b/llama_stack/providers/tests/scoring/conftest.py @@ -55,21 +55,6 @@ def pytest_configure(config): ) -def pytest_addoption(parser): - parser.addoption( - "--inference-model", - action="store", - default="meta-llama/Llama-3.2-3B-Instruct", - help="Specify the inference model to use for testing", - ) - parser.addoption( - "--judge-model", - action="store", - default="meta-llama/Llama-3.1-8B-Instruct", - help="Specify the judge model to use for testing", - ) - - def pytest_generate_tests(metafunc): judge_model = metafunc.config.getoption("--judge-model") if "judge_model" in metafunc.fixturenames: diff --git a/llama_stack/providers/tests/tools/conftest.py b/llama_stack/providers/tests/tools/conftest.py index 11aad5ab6..525abe8ab 100644 --- a/llama_stack/providers/tests/tools/conftest.py +++ b/llama_stack/providers/tests/tools/conftest.py @@ -34,21 +34,6 @@ def pytest_configure(config): ) -def pytest_addoption(parser): - parser.addoption( - "--inference-model", - action="store", - default="meta-llama/Llama-3.2-3B-Instruct", - help="Specify the inference model to use for testing", - ) - parser.addoption( - "--safety-shield", - action="store", - default="meta-llama/Llama-Guard-3-1B", - help="Specify the safety shield to use for testing", - ) - - def pytest_generate_tests(metafunc): if "tools_stack" in metafunc.fixturenames: available_fixtures = { From 59eeaf7f8114916e76536ea94bb2507cbc86c3c7 Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 16 Jan 2025 12:08:46 -0800 Subject: [PATCH 032/107] Idiomatic REST API: Telemetry (#786) # What does this PR do? Changes Telemetry API to follow more idiomatic REST - [ ] Addresses issue (#issue) ## Test Plan TBD, once i get an approval for rest endpoints --- docs/resources/llama-stack-spec.html | 954 ++++++++++-------- docs/resources/llama-stack-spec.yaml | 562 ++++++----- llama_stack/apis/telemetry/telemetry.py | 36 +- .../telemetry/meta_reference/telemetry.py | 38 +- .../utils/telemetry/dataset_mixin.py | 12 +- .../utils/telemetry/sqlite_trace_store.py | 22 +- 6 files changed, 955 insertions(+), 669 deletions(-) diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index 2db33c87a..750dce798 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -771,7 +771,7 @@ } } }, - "/v1/eval/evaluate-rows": { + "/v1/eval/tasks/{task_id}/evaluations": { "post": { "responses": { "200": { @@ -789,6 +789,14 @@ "Eval" ], "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -1392,6 +1400,127 @@ ] } }, + "/v1/telemetry/traces/{trace_id}/spans/{span_id}": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Span" + } + } + } + } + }, + "tags": [ + "Telemetry" + ], + "parameters": [ + { + "name": "trace_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "span_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + } + }, + "/v1/telemetry/spans/{span_id}/tree": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuerySpanTreeResponse" + } + } + } + } + }, + "tags": [ + "Telemetry" + ], + "parameters": [ + { + "name": "span_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "attributes_to_return", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "max_depth", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + } + }, "/v1/tools/{tool_name}": { "get": { "responses": { @@ -1525,6 +1654,53 @@ ] } }, + "/v1/telemetry/traces/{trace_id}": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Trace" + } + } + } + } + }, + "tags": [ + "Telemetry" + ], + "parameters": [ + { + "name": "trace_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + } + }, "/v1/post-training/job/artifacts": { "get": { "responses": { @@ -1803,17 +1979,47 @@ } } }, - "/v1/eval/jobs/cancel": { - "post": { + "/v1/eval/tasks/{task_id}/jobs/{job_id}": { + "get": { "responses": { "200": { - "description": "OK" + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/JobStatus" + }, + { + "type": "null" + } + ] + } + } + } } }, "tags": [ "Eval" ], "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -1832,20 +2038,56 @@ "type": "string" } } + ] + }, + "delete": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "Eval" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JobCancelRequest" - } + "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string" } }, - "required": true - } + { + "name": "job_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] } }, - "/v1/eval/jobs/{job_id}/result": { + "/v1/eval/tasks/{task_id}/jobs/{job_id}/result": { "get": { "responses": { "200": { @@ -1873,74 +2115,12 @@ }, { "name": "task_id", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ] - } - }, - "/v1/eval/jobs/{job_id}": { - "get": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/JobStatus" - }, - { - "type": "null" - } - ] - } - } - } - } - }, - "tags": [ - "Eval" - ], - "parameters": [ - { - "name": "job_id", "in": "path", "required": true, "schema": { "type": "string" } }, - { - "name": "task_id", - "in": "query", - "required": true, - "schema": { - "type": "string" - } - }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -2305,7 +2485,7 @@ } } }, - "/v1/providers/list": { + "/v1/inspect/providers": { "get": { "responses": { "200": { @@ -2344,7 +2524,7 @@ ] } }, - "/v1/routes/list": { + "/v1/inspect/routes": { "get": { "responses": { "200": { @@ -2352,13 +2532,7 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "$ref": "#/components/schemas/RouteInfo" - } - } + "$ref": "#/components/schemas/ListRoutesResponse" } } } @@ -2738,7 +2912,7 @@ ] } }, - "/v1/telemetry/log-event": { + "/v1/telemetry/events": { "post": { "responses": { "200": { @@ -2878,18 +3052,15 @@ } } }, - "/v1/telemetry/query-span-tree": { - "post": { + "/v1/telemetry/spans": { + "get": { "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": { - "$ref": "#/components/schemas/SpanWithStatus" - } + "$ref": "#/components/schemas/QuerySpansResponse" } } } @@ -2899,6 +3070,36 @@ "Telemetry" ], "parameters": [ + { + "name": "attribute_filters", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QueryCondition" + } + } + }, + { + "name": "attributes_to_return", + "in": "query", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + { + "name": "max_depth", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -2917,28 +3118,18 @@ "type": "string" } } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QuerySpanTreeRequest" - } - } - }, - "required": true - } + ] } }, - "/v1/telemetry/query-spans": { - "post": { + "/v1/telemetry/traces": { + "get": { "responses": { "200": { "description": "OK", "content": { - "application/jsonl": { + "application/json": { "schema": { - "$ref": "#/components/schemas/Span" + "$ref": "#/components/schemas/QueryTracesResponse" } } } @@ -2948,6 +3139,44 @@ "Telemetry" ], "parameters": [ + { + "name": "attribute_filters", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/QueryCondition" + } + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "offset", + "in": "query", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "order_by", + "in": "query", + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -2966,69 +3195,10 @@ "type": "string" } } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QuerySpansRequest" - } - } - }, - "required": true - } + ] } }, - "/v1/telemetry/query-traces": { - "post": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/jsonl": { - "schema": { - "$ref": "#/components/schemas/Trace" - } - } - } - } - }, - "tags": [ - "Telemetry" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QueryTracesRequest" - } - } - }, - "required": true - } - } - }, - "/v1/eval/run": { + "/v1/eval/tasks/{task_id}/jobs": { "post": { "responses": { "200": { @@ -3046,6 +3216,14 @@ "Eval" ], "parameters": [ + { + "name": "task_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "X-LlamaStack-Provider-Data", "in": "header", @@ -3126,7 +3304,7 @@ } } }, - "/v1/telemetry/save-spans-to-dataset": { + "/v1/telemetry/spans/export": { "post": { "responses": { "200": { @@ -5514,9 +5692,6 @@ "EvaluateRowsRequest": { "type": "object", "properties": { - "task_id": { - "type": "string" - }, "input_rows": { "type": "array", "items": { @@ -5564,7 +5739,6 @@ }, "additionalProperties": false, "required": [ - "task_id", "input_rows", "scoring_functions", "task_config" @@ -6395,6 +6569,145 @@ ], "title": "A safety shield resource that can be used to check content" }, + "Span": { + "type": "object", + "properties": { + "span_id": { + "type": "string" + }, + "trace_id": { + "type": "string" + }, + "parent_span_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "start_time": { + "type": "string", + "format": "date-time" + }, + "end_time": { + "type": "string", + "format": "date-time" + }, + "attributes": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "array" + }, + { + "type": "object" + } + ] + } + } + }, + "additionalProperties": false, + "required": [ + "span_id", + "trace_id", + "name", + "start_time" + ] + }, + "SpanStatus": { + "type": "string", + "enum": [ + "ok", + "error" + ] + }, + "SpanWithStatus": { + "type": "object", + "properties": { + "span_id": { + "type": "string" + }, + "trace_id": { + "type": "string" + }, + "parent_span_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "start_time": { + "type": "string", + "format": "date-time" + }, + "end_time": { + "type": "string", + "format": "date-time" + }, + "attributes": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "array" + }, + { + "type": "object" + } + ] + } + }, + "status": { + "$ref": "#/components/schemas/SpanStatus" + } + }, + "additionalProperties": false, + "required": [ + "span_id", + "trace_id", + "name", + "start_time" + ] + }, + "QuerySpanTreeResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "$ref": "#/components/schemas/SpanWithStatus" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, "Tool": { "type": "object", "properties": { @@ -6527,6 +6840,31 @@ "type" ] }, + "Trace": { + "type": "object", + "properties": { + "trace_id": { + "type": "string" + }, + "root_span_id": { + "type": "string" + }, + "start_time": { + "type": "string", + "format": "date-time" + }, + "end_time": { + "type": "string", + "format": "date-time" + } + }, + "additionalProperties": false, + "required": [ + "trace_id", + "root_span_id", + "start_time" + ] + }, "Checkpoint": { "description": "Checkpoint created during training runs" }, @@ -6795,22 +7133,6 @@ "content" ] }, - "JobCancelRequest": { - "type": "object", - "properties": { - "job_id": { - "type": "string" - }, - "task_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "job_id", - "task_id" - ] - }, "ListDatasetsResponse": { "type": "object", "properties": { @@ -6925,6 +7247,21 @@ "provider_types" ] }, + "ListRoutesResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/RouteInfo" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, "ListScoringFunctionsResponse": { "type": "object", "properties": { @@ -7106,13 +7443,6 @@ "name" ] }, - "SpanStatus": { - "type": "string", - "enum": [ - "ok", - "error" - ] - }, "StructuredLogEvent": { "type": "object", "properties": { @@ -7589,87 +7919,6 @@ "scores" ] }, - "QuerySpanTreeRequest": { - "type": "object", - "properties": { - "span_id": { - "type": "string" - }, - "attributes_to_return": { - "type": "array", - "items": { - "type": "string" - } - }, - "max_depth": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "span_id" - ] - }, - "SpanWithStatus": { - "type": "object", - "properties": { - "span_id": { - "type": "string" - }, - "trace_id": { - "type": "string" - }, - "parent_span_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "start_time": { - "type": "string", - "format": "date-time" - }, - "end_time": { - "type": "string", - "format": "date-time" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "null" - }, - { - "type": "boolean" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "array" - }, - { - "type": "object" - } - ] - } - }, - "status": { - "$ref": "#/components/schemas/SpanStatus" - } - }, - "additionalProperties": false, - "required": [ - "span_id", - "trace_id", - "name", - "start_time" - ] - }, "QueryCondition": { "type": "object", "properties": { @@ -7718,135 +7967,34 @@ "lt" ] }, - "QuerySpansRequest": { + "QuerySpansResponse": { "type": "object", "properties": { - "attribute_filters": { + "data": { "type": "array", "items": { - "$ref": "#/components/schemas/QueryCondition" + "$ref": "#/components/schemas/Span" } - }, - "attributes_to_return": { - "type": "array", - "items": { - "type": "string" - } - }, - "max_depth": { - "type": "integer" } }, "additionalProperties": false, "required": [ - "attribute_filters", - "attributes_to_return" + "data" ] }, - "Span": { + "QueryTracesResponse": { "type": "object", "properties": { - "span_id": { - "type": "string" - }, - "trace_id": { - "type": "string" - }, - "parent_span_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "start_time": { - "type": "string", - "format": "date-time" - }, - "end_time": { - "type": "string", - "format": "date-time" - }, - "attributes": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "null" - }, - { - "type": "boolean" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "array" - }, - { - "type": "object" - } - ] + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Trace" } } }, "additionalProperties": false, "required": [ - "span_id", - "trace_id", - "name", - "start_time" - ] - }, - "QueryTracesRequest": { - "type": "object", - "properties": { - "attribute_filters": { - "type": "array", - "items": { - "$ref": "#/components/schemas/QueryCondition" - } - }, - "limit": { - "type": "integer" - }, - "offset": { - "type": "integer" - }, - "order_by": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "Trace": { - "type": "object", - "properties": { - "trace_id": { - "type": "string" - }, - "root_span_id": { - "type": "string" - }, - "start_time": { - "type": "string", - "format": "date-time" - }, - "end_time": { - "type": "string", - "format": "date-time" - } - }, - "additionalProperties": false, - "required": [ - "trace_id", - "root_span_id", - "start_time" + "data" ] }, "RegisterDatasetRequest": { @@ -8234,9 +8382,6 @@ "RunEvalRequest": { "type": "object", "properties": { - "task_id": { - "type": "string" - }, "task_config": { "oneOf": [ { @@ -8250,7 +8395,6 @@ }, "additionalProperties": false, "required": [ - "task_id", "task_config" ] }, @@ -9014,10 +9158,6 @@ "name": "Job", "description": "" }, - { - "name": "JobCancelRequest", - "description": "" - }, { "name": "JobStatus", "description": "" @@ -9066,6 +9206,10 @@ "name": "ListProvidersResponse", "description": "" }, + { + "name": "ListRoutesResponse", + "description": "" + }, { "name": "ListScoringFunctionsResponse", "description": "" @@ -9195,16 +9339,16 @@ "description": "" }, { - "name": "QuerySpanTreeRequest", - "description": "" + "name": "QuerySpanTreeResponse", + "description": "" }, { - "name": "QuerySpansRequest", - "description": "" + "name": "QuerySpansResponse", + "description": "" }, { - "name": "QueryTracesRequest", - "description": "" + "name": "QueryTracesResponse", + "description": "" }, { "name": "RegexParserScoringFnParams", @@ -9588,7 +9732,6 @@ "InterleavedContentItem", "InvokeToolRequest", "Job", - "JobCancelRequest", "JobStatus", "KeyValueMemoryBank", "KeyValueMemoryBankParams", @@ -9601,6 +9744,7 @@ "ListModelsResponse", "ListPostTrainingJobsResponse", "ListProvidersResponse", + "ListRoutesResponse", "ListScoringFunctionsResponse", "ListShieldsResponse", "ListToolGroupsResponse", @@ -9630,9 +9774,9 @@ "QueryConditionOp", "QueryDocumentsRequest", "QueryDocumentsResponse", - "QuerySpanTreeRequest", - "QuerySpansRequest", - "QueryTracesRequest", + "QuerySpanTreeResponse", + "QuerySpansResponse", + "QueryTracesResponse", "RegexParserScoringFnParams", "RegisterDatasetRequest", "RegisterEvalTaskRequest", diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index ab27e4f3d..9d5f9cd60 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -836,10 +836,7 @@ components: oneOf: - $ref: '#/components/schemas/BenchmarkEvalTaskConfig' - $ref: '#/components/schemas/AppEvalTaskConfig' - task_id: - type: string required: - - task_id - input_rows - scoring_functions - task_config @@ -1003,17 +1000,6 @@ components: required: - job_id type: object - JobCancelRequest: - additionalProperties: false - properties: - job_id: - type: string - task_id: - type: string - required: - - job_id - - task_id - type: object JobStatus: enum: - completed @@ -1178,6 +1164,16 @@ components: required: - data type: object + ListRoutesResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/RouteInfo' + type: array + required: + - data + type: object ListScoringFunctionsResponse: additionalProperties: false properties: @@ -1781,52 +1777,35 @@ components: - chunks - scores type: object - QuerySpanTreeRequest: + QuerySpanTreeResponse: additionalProperties: false properties: - attributes_to_return: - items: - type: string - type: array - max_depth: - type: integer - span_id: - type: string + data: + additionalProperties: + $ref: '#/components/schemas/SpanWithStatus' + type: object required: - - span_id + - data type: object - QuerySpansRequest: + QuerySpansResponse: additionalProperties: false properties: - attribute_filters: + data: items: - $ref: '#/components/schemas/QueryCondition' + $ref: '#/components/schemas/Span' type: array - attributes_to_return: - items: - type: string - type: array - max_depth: - type: integer required: - - attribute_filters - - attributes_to_return + - data type: object - QueryTracesRequest: + QueryTracesResponse: additionalProperties: false properties: - attribute_filters: + data: items: - $ref: '#/components/schemas/QueryCondition' - type: array - limit: - type: integer - offset: - type: integer - order_by: - items: - type: string + $ref: '#/components/schemas/Trace' type: array + required: + - data type: object RegexParserScoringFnParams: additionalProperties: false @@ -2082,10 +2061,7 @@ components: oneOf: - $ref: '#/components/schemas/BenchmarkEvalTaskConfig' - $ref: '#/components/schemas/AppEvalTaskConfig' - task_id: - type: string required: - - task_id - task_config type: object RunShieldRequest: @@ -3916,9 +3892,14 @@ paths: description: OK tags: - EvalTasks - /v1/eval/evaluate-rows: + /v1/eval/tasks/{task_id}/evaluations: post: parameters: + - in: path + name: task_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -3948,9 +3929,14 @@ paths: description: OK tags: - Eval - /v1/eval/jobs/cancel: + /v1/eval/tasks/{task_id}/jobs: post: parameters: + - in: path + name: task_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -3969,26 +3955,61 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/JobCancelRequest' + $ref: '#/components/schemas/RunEvalRequest' required: true responses: '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Job' description: OK tags: - Eval - /v1/eval/jobs/{job_id}: - get: + /v1/eval/tasks/{task_id}/jobs/{job_id}: + delete: parameters: + - in: path + name: task_id + required: true + schema: + type: string - in: path name: job_id required: true schema: type: string - - in: query + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + description: OK + tags: + - Eval + get: + parameters: + - in: path name: task_id required: true schema: type: string + - in: path + name: job_id + required: true + schema: + type: string - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -4014,7 +4035,7 @@ paths: description: OK tags: - Eval - /v1/eval/jobs/{job_id}/result: + /v1/eval/tasks/{task_id}/jobs/{job_id}/result: get: parameters: - in: path @@ -4022,7 +4043,7 @@ paths: required: true schema: type: string - - in: query + - in: path name: task_id required: true schema: @@ -4050,38 +4071,6 @@ paths: description: OK tags: - Eval - /v1/eval/run: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RunEvalRequest' - required: true - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/Job' - description: OK - tags: - - Eval /v1/health: get: parameters: @@ -4208,6 +4197,58 @@ paths: description: OK tags: - Inference + /v1/inspect/providers: + get: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ListProvidersResponse' + description: OK + tags: + - Inspect + /v1/inspect/routes: + get: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ListRoutesResponse' + description: OK + tags: + - Inspect /v1/memory-banks: get: parameters: @@ -4688,62 +4729,6 @@ paths: description: OK tags: - PostTraining (Coming Soon) - /v1/providers/list: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ListProvidersResponse' - description: OK - tags: - - Inspect - /v1/routes/list: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/json: - schema: - additionalProperties: - items: - $ref: '#/components/schemas/RouteInfo' - type: array - type: object - description: OK - tags: - - Inspect /v1/safety/run-shield: post: parameters: @@ -5048,7 +5033,7 @@ paths: description: OK tags: - SyntheticDataGeneration (Coming Soon) - /v1/telemetry/log-event: + /v1/telemetry/events: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5076,9 +5061,28 @@ paths: description: OK tags: - Telemetry - /v1/telemetry/query-span-tree: - post: + /v1/telemetry/spans: + get: parameters: + - in: query + name: attribute_filters + required: true + schema: + items: + $ref: '#/components/schemas/QueryCondition' + type: array + - in: query + name: attributes_to_return + required: true + schema: + items: + type: string + type: array + - in: query + name: max_depth + required: false + schema: + type: integer - description: JSON-encoded provider data which will be made available to the adapter servicing the API in: header @@ -5093,88 +5097,16 @@ paths: required: false schema: type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/QuerySpanTreeRequest' - required: true responses: '200': content: application/json: schema: - additionalProperties: - $ref: '#/components/schemas/SpanWithStatus' - type: object + $ref: '#/components/schemas/QuerySpansResponse' description: OK tags: - Telemetry - /v1/telemetry/query-spans: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/QuerySpansRequest' - required: true - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/Span' - description: OK - tags: - - Telemetry - /v1/telemetry/query-traces: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/QueryTracesRequest' - required: true - responses: - '200': - content: - application/jsonl: - schema: - $ref: '#/components/schemas/Trace' - description: OK - tags: - - Telemetry - /v1/telemetry/save-spans-to-dataset: + /v1/telemetry/spans/export: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5202,6 +5134,166 @@ paths: description: OK tags: - Telemetry + /v1/telemetry/spans/{span_id}/tree: + get: + parameters: + - in: path + name: span_id + required: true + schema: + type: string + - in: query + name: attributes_to_return + required: false + schema: + items: + type: string + type: array + - in: query + name: max_depth + required: false + schema: + type: integer + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/QuerySpanTreeResponse' + description: OK + tags: + - Telemetry + /v1/telemetry/traces: + get: + parameters: + - in: query + name: attribute_filters + required: false + schema: + items: + $ref: '#/components/schemas/QueryCondition' + type: array + - in: query + name: limit + required: false + schema: + type: integer + - in: query + name: offset + required: false + schema: + type: integer + - in: query + name: order_by + required: false + schema: + items: + type: string + type: array + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/QueryTracesResponse' + description: OK + tags: + - Telemetry + /v1/telemetry/traces/{trace_id}: + get: + parameters: + - in: path + name: trace_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Trace' + description: OK + tags: + - Telemetry + /v1/telemetry/traces/{trace_id}/spans/{span_id}: + get: + parameters: + - in: path + name: trace_id + required: true + schema: + type: string + - in: path + name: span_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Span' + description: OK + tags: + - Telemetry /v1/tool-runtime/invoke: post: parameters: @@ -5673,9 +5765,6 @@ tags: name: InvokeToolRequest - description: name: Job -- description: - name: JobCancelRequest - description: name: JobStatus - description: name: ListProvidersResponse +- description: + name: ListRoutesResponse - description: name: ListScoringFunctionsResponse @@ -5797,15 +5889,15 @@ tags: - description: name: QueryDocumentsResponse -- description: - name: QuerySpanTreeRequest -- description: - name: QuerySpansRequest -- description: - name: QueryTracesRequest + name: QueryTracesResponse - description: name: RegexParserScoringFnParams @@ -6085,7 +6177,6 @@ x-tagGroups: - InterleavedContentItem - InvokeToolRequest - Job - - JobCancelRequest - JobStatus - KeyValueMemoryBank - KeyValueMemoryBankParams @@ -6098,6 +6189,7 @@ x-tagGroups: - ListModelsResponse - ListPostTrainingJobsResponse - ListProvidersResponse + - ListRoutesResponse - ListScoringFunctionsResponse - ListShieldsResponse - ListToolGroupsResponse @@ -6127,9 +6219,9 @@ x-tagGroups: - QueryConditionOp - QueryDocumentsRequest - QueryDocumentsResponse - - QuerySpanTreeRequest - - QuerySpansRequest - - QueryTracesRequest + - QuerySpanTreeResponse + - QuerySpansResponse + - QueryTracesResponse - RegexParserScoringFnParams - RegisterDatasetRequest - RegisterEvalTaskRequest diff --git a/llama_stack/apis/telemetry/telemetry.py b/llama_stack/apis/telemetry/telemetry.py index d04cb67e3..30a4e2342 100644 --- a/llama_stack/apis/telemetry/telemetry.py +++ b/llama_stack/apis/telemetry/telemetry.py @@ -169,39 +169,57 @@ class QueryCondition(BaseModel): value: Any +class QueryTracesResponse(BaseModel): + data: List[Trace] + + +class QuerySpansResponse(BaseModel): + data: List[Span] + + +class QuerySpanTreeResponse(BaseModel): + data: Dict[str, SpanWithStatus] + + @runtime_checkable class Telemetry(Protocol): - @webmethod(route="/telemetry/log-event") + @webmethod(route="/telemetry/events", method="POST") async def log_event( self, event: Event, ttl_seconds: int = DEFAULT_TTL_DAYS * 86400 ) -> None: ... - @webmethod(route="/telemetry/query-traces", method="POST") + @webmethod(route="/telemetry/traces", method="GET") async def query_traces( self, attribute_filters: Optional[List[QueryCondition]] = None, limit: Optional[int] = 100, offset: Optional[int] = 0, order_by: Optional[List[str]] = None, - ) -> List[Trace]: ... + ) -> QueryTracesResponse: ... - @webmethod(route="/telemetry/query-span-tree", method="POST") - async def query_span_tree( + @webmethod(route="/telemetry/traces/{trace_id}", method="GET") + async def get_trace(self, trace_id: str) -> Trace: ... + + @webmethod(route="/telemetry/traces/{trace_id}/spans/{span_id}", method="GET") + async def get_span(self, trace_id: str, span_id: str) -> Span: ... + + @webmethod(route="/telemetry/spans/{span_id}/tree", method="GET") + async def get_span_tree( self, span_id: str, attributes_to_return: Optional[List[str]] = None, max_depth: Optional[int] = None, - ) -> Dict[str, SpanWithStatus]: ... + ) -> QuerySpanTreeResponse: ... - @webmethod(route="/telemetry/query-spans", method="POST") + @webmethod(route="/telemetry/spans", method="GET") async def query_spans( self, attribute_filters: List[QueryCondition], attributes_to_return: List[str], max_depth: Optional[int] = None, - ) -> List[Span]: ... + ) -> QuerySpansResponse: ... - @webmethod(route="/telemetry/save-spans-to-dataset", method="POST") + @webmethod(route="/telemetry/spans/export", method="POST") async def save_spans_to_dataset( self, attribute_filters: List[QueryCondition], diff --git a/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py b/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py index e2e318375..4875f8cf0 100644 --- a/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py +++ b/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py @@ -21,10 +21,12 @@ from llama_stack.apis.telemetry import ( Event, MetricEvent, QueryCondition, + QuerySpanTreeResponse, + QueryTracesResponse, + Span, SpanEndPayload, SpanStartPayload, SpanStatus, - SpanWithStatus, StructuredLogEvent, Telemetry, Trace, @@ -241,22 +243,32 @@ class TelemetryAdapter(TelemetryDatasetMixin, Telemetry): limit: Optional[int] = 100, offset: Optional[int] = 0, order_by: Optional[List[str]] = None, - ) -> List[Trace]: - return await self.trace_store.query_traces( - attribute_filters=attribute_filters, - limit=limit, - offset=offset, - order_by=order_by, + ) -> QueryTracesResponse: + return QueryTracesResponse( + data=await self.trace_store.query_traces( + attribute_filters=attribute_filters, + limit=limit, + offset=offset, + order_by=order_by, + ) ) - async def query_span_tree( + async def get_trace(self, trace_id: str) -> Trace: + return await self.trace_store.get_trace(trace_id) + + async def get_span(self, trace_id: str, span_id: str) -> Span: + return await self.trace_store.get_span(trace_id, span_id) + + async def get_span_tree( self, span_id: str, attributes_to_return: Optional[List[str]] = None, max_depth: Optional[int] = None, - ) -> Dict[str, SpanWithStatus]: - return await self.trace_store.get_span_tree( - span_id=span_id, - attributes_to_return=attributes_to_return, - max_depth=max_depth, + ) -> QuerySpanTreeResponse: + return QuerySpanTreeResponse( + data=await self.trace_store.get_span_tree( + span_id=span_id, + attributes_to_return=attributes_to_return, + max_depth=max_depth, + ) ) diff --git a/llama_stack/providers/utils/telemetry/dataset_mixin.py b/llama_stack/providers/utils/telemetry/dataset_mixin.py index e488f2475..6806f39aa 100644 --- a/llama_stack/providers/utils/telemetry/dataset_mixin.py +++ b/llama_stack/providers/utils/telemetry/dataset_mixin.py @@ -7,7 +7,7 @@ from typing import List, Optional from llama_stack.apis.datasetio import DatasetIO -from llama_stack.apis.telemetry import QueryCondition, Span +from llama_stack.apis.telemetry import QueryCondition, QuerySpansResponse, Span class TelemetryDatasetMixin: @@ -48,18 +48,18 @@ class TelemetryDatasetMixin: attribute_filters: List[QueryCondition], attributes_to_return: List[str], max_depth: Optional[int] = None, - ) -> List[Span]: + ) -> QuerySpansResponse: traces = await self.query_traces(attribute_filters=attribute_filters) spans = [] - for trace in traces: - spans_by_id = await self.query_span_tree( + for trace in traces.data: + spans_by_id_resp = await self.get_span_tree( span_id=trace.root_span_id, attributes_to_return=attributes_to_return, max_depth=max_depth, ) - for span in spans_by_id.values(): + for span in spans_by_id_resp.data.values(): if span.attributes and all( attr in span.attributes and span.attributes[attr] is not None for attr in attributes_to_return @@ -76,4 +76,4 @@ class TelemetryDatasetMixin: ) ) - return spans + return QuerySpansResponse(data=spans) diff --git a/llama_stack/providers/utils/telemetry/sqlite_trace_store.py b/llama_stack/providers/utils/telemetry/sqlite_trace_store.py index b0c3f7868..a2821da43 100644 --- a/llama_stack/providers/utils/telemetry/sqlite_trace_store.py +++ b/llama_stack/providers/utils/telemetry/sqlite_trace_store.py @@ -10,7 +10,7 @@ from typing import Dict, List, Optional, Protocol import aiosqlite -from llama_stack.apis.telemetry import QueryCondition, SpanWithStatus, Trace +from llama_stack.apis.telemetry import QueryCondition, Span, SpanWithStatus, Trace class TraceStore(Protocol): @@ -167,3 +167,23 @@ class SQLiteTraceStore(TraceStore): spans_by_id[span.span_id] = span return spans_by_id + + async def get_trace(self, trace_id: str) -> Trace: + query = "SELECT * FROM traces WHERE trace_id = ?" + async with aiosqlite.connect(self.conn_string) as conn: + conn.row_factory = aiosqlite.Row + async with conn.execute(query, (trace_id,)) as cursor: + row = await cursor.fetchone() + if row is None: + raise ValueError(f"Trace {trace_id} not found") + return Trace(**row) + + async def get_span(self, trace_id: str, span_id: str) -> Span: + query = "SELECT * FROM spans WHERE trace_id = ? AND span_id = ?" + async with aiosqlite.connect(self.conn_string) as conn: + conn.row_factory = aiosqlite.Row + async with conn.execute(query, (trace_id, span_id)) as cursor: + row = await cursor.fetchone() + if row is None: + raise ValueError(f"Span {span_id} not found") + return Span(**row) From cee3816609cb7687f7a26d52b53df08c2ee953db Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Thu, 16 Jan 2025 13:44:53 -0800 Subject: [PATCH 033/107] Make llama stack build not create a new conda by default (#788) ## What does this PR do? So far `llama stack build` has always created a separate conda environment for packaging the dependencies of a distribution. The main reason to do so is isolation -- distributions are composed of providers which can have a variety of potentially conflicting dependencies. That said, this has created significant annoyance for new users since it is not at all transparent. The fact that `llama stack run` is actually running the code in some other conda is very surprising. This PR tries to make things better. - Both `llama stack build` and `llama stack run` now accept an `--image-name` argument which represents the (conda, docker, virtualenv) image you want to operate upon. - For the default (conda) mode, the script checks if a current conda environment exists. If one exists, it uses it. - If `--image-name` is provided, that option is used. In this case, an environment is created if needed. - There is no automatic `llamastack-` prefixing of the environment names done anymore. ## Test Plan Start in a conda environment, run `llama stack build --template fireworks`; verify that it successfully built into the current environment and stored the build file at `$CONDA_PREFIX/llamastack-build.yaml`. Run `llama stack run fireworks` which started correctly in the current environment. Ran the same build command outside of conda. It failed asking for `--image-name`. Ran it with `llama stack build --template fireworks --image-name foo`. This successfully created a conda environment called `foo` and installed deps. Ran `llama stack run fireworks` outside conda which failed. Activated a different conda, ran again, it failed saying it did not find the `llamastack-build.yaml` file. Then used `--image-name foo` option and it ran successfully. --- llama_stack/cli/stack/_build.py | 307 ++++++++++++++++++ llama_stack/cli/stack/build.py | 292 +---------------- llama_stack/cli/stack/run.py | 63 +++- llama_stack/distribution/build.py | 20 +- llama_stack/distribution/build_conda_env.sh | 11 +- llama_stack/distribution/datatypes.py | 6 +- llama_stack/distribution/start_conda_env.sh | 3 +- .../quantization/scripts/build_conda.sh | 36 -- 8 files changed, 400 insertions(+), 338 deletions(-) create mode 100644 llama_stack/cli/stack/_build.py delete mode 100644 llama_stack/providers/inline/inference/meta_reference/quantization/scripts/build_conda.sh diff --git a/llama_stack/cli/stack/_build.py b/llama_stack/cli/stack/_build.py new file mode 100644 index 000000000..08c987a50 --- /dev/null +++ b/llama_stack/cli/stack/_build.py @@ -0,0 +1,307 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +import argparse +import importlib.resources +import json +import os +import shutil +import textwrap +from functools import lru_cache +from pathlib import Path +from typing import Dict, Optional + +import yaml +from prompt_toolkit import prompt +from prompt_toolkit.completion import WordCompleter +from prompt_toolkit.validation import Validator +from termcolor import cprint + +from llama_stack.cli.table import print_table + +from llama_stack.distribution.build import build_image, ImageType +from llama_stack.distribution.datatypes import ( + BuildConfig, + DistributionSpec, + Provider, + StackRunConfig, +) +from llama_stack.distribution.distribution import get_provider_registry +from llama_stack.distribution.resolver import InvalidProviderError +from llama_stack.distribution.utils.config_dirs import DISTRIBS_BASE_DIR +from llama_stack.distribution.utils.dynamic import instantiate_class_type +from llama_stack.providers.datatypes import Api + + +TEMPLATES_PATH = Path(__file__).parent.parent.parent / "templates" + + +@lru_cache() +def available_templates_specs() -> Dict[str, BuildConfig]: + import yaml + + template_specs = {} + for p in TEMPLATES_PATH.rglob("*build.yaml"): + template_name = p.parent.name + with open(p, "r") as f: + build_config = BuildConfig(**yaml.safe_load(f)) + template_specs[template_name] = build_config + return template_specs + + +def run_stack_build_command( + parser: argparse.ArgumentParser, args: argparse.Namespace +) -> None: + if args.list_templates: + return _run_template_list_cmd() + + current_conda_env = os.environ.get("CONDA_DEFAULT_ENV") + image_name = args.image_name or current_conda_env + + if args.template: + available_templates = available_templates_specs() + if args.template not in available_templates: + cprint( + f"Could not find template {args.template}. Please run `llama stack build --list-templates` to check out the available templates", + color="red", + ) + return + build_config = available_templates[args.template] + if args.image_type: + build_config.image_type = args.image_type + else: + cprint( + f"Please specify a image-type (docker | conda | venv) for {args.template}", + color="red", + ) + return + _run_stack_build_command_from_build_config( + build_config, + image_name=image_name, + template_name=args.template, + ) + return + + if not args.config and not args.template: + name = prompt( + "> Enter a name for your Llama Stack (e.g. my-local-stack): ", + validator=Validator.from_callable( + lambda x: len(x) > 0, + error_message="Name cannot be empty, please enter a name", + ), + ) + + image_type = prompt( + "> Enter the image type you want your Llama Stack to be built as (docker or conda or venv): ", + validator=Validator.from_callable( + lambda x: x in ["docker", "conda", "venv"], + error_message="Invalid image type, please enter conda or docker or venv", + ), + default="conda", + ) + + if image_type == "conda": + if not image_name: + cprint( + f"No current conda environment detected or specified, will create a new conda environment with the name `llamastack-{name}`", + color="yellow", + ) + image_name = f"llamastack-{name}" + else: + cprint( + f"Using conda environment {image_name}", + color="green", + ) + + cprint( + textwrap.dedent( + """ + Llama Stack is composed of several APIs working together. Let's select + the provider types (implementations) you want to use for these APIs. + """, + ), + color="green", + ) + + print("Tip: use to see options for the providers.\n") + + providers = dict() + for api, providers_for_api in get_provider_registry().items(): + available_providers = [ + x + for x in providers_for_api.keys() + if x not in ("remote", "remote::sample") + ] + api_provider = prompt( + "> Enter provider for API {}: ".format(api.value), + completer=WordCompleter(available_providers), + complete_while_typing=True, + validator=Validator.from_callable( + lambda x: x in available_providers, + error_message="Invalid provider, use to see options", + ), + ) + + providers[api.value] = api_provider + + description = prompt( + "\n > (Optional) Enter a short description for your Llama Stack: ", + default="", + ) + + distribution_spec = DistributionSpec( + providers=providers, + description=description, + ) + + build_config = BuildConfig( + image_type=image_type, distribution_spec=distribution_spec + ) + else: + with open(args.config, "r") as f: + try: + build_config = BuildConfig(**yaml.safe_load(f)) + except Exception as e: + cprint( + f"Could not parse config file {args.config}: {e}", + color="red", + ) + return + + _run_stack_build_command_from_build_config(build_config, image_name=image_name) + + +def _generate_run_config( + build_config: BuildConfig, build_dir: Path, image_name: str +) -> None: + """ + Generate a run.yaml template file for user to edit from a build.yaml file + """ + apis = list(build_config.distribution_spec.providers.keys()) + run_config = StackRunConfig( + docker_image=( + image_name if build_config.image_type == ImageType.docker.value else None + ), + image_name=image_name, + apis=apis, + providers={}, + ) + # build providers dict + provider_registry = get_provider_registry() + for api in apis: + run_config.providers[api] = [] + provider_types = build_config.distribution_spec.providers[api] + if isinstance(provider_types, str): + provider_types = [provider_types] + + for i, provider_type in enumerate(provider_types): + pid = provider_type.split("::")[-1] + + p = provider_registry[Api(api)][provider_type] + if p.deprecation_error: + raise InvalidProviderError(p.deprecation_error) + + config_type = instantiate_class_type( + provider_registry[Api(api)][provider_type].config_class + ) + if hasattr(config_type, "sample_run_config"): + config = config_type.sample_run_config( + __distro_dir__=f"distributions/{image_name}" + ) + else: + config = {} + + p_spec = Provider( + provider_id=f"{pid}-{i}" if len(provider_types) > 1 else pid, + provider_type=provider_type, + config=config, + ) + run_config.providers[api].append(p_spec) + + run_config_file = build_dir / f"{image_name}-run.yaml" + + with open(run_config_file, "w") as f: + to_write = json.loads(run_config.model_dump_json()) + f.write(yaml.dump(to_write, sort_keys=False)) + + cprint( + f"You can now edit {run_config_file} and run `llama stack run {image_name}`", + color="green", + ) + + +def _run_stack_build_command_from_build_config( + build_config: BuildConfig, + image_name: Optional[str] = None, + template_name: Optional[str] = None, +) -> None: + if build_config.image_type == ImageType.docker.value: + if template_name: + image_name = f"distribution-{template_name}" + else: + if not image_name: + raise ValueError( + "Please specify an image name when building a docker image without a template" + ) + elif build_config.image_type == ImageType.conda.value: + if not image_name: + raise ValueError("Please specify an image name when building a conda image") + + if template_name: + build_dir = DISTRIBS_BASE_DIR / template_name + build_file_path = build_dir / f"{template_name}-build.yaml" + else: + build_dir = DISTRIBS_BASE_DIR / image_name + build_file_path = build_dir / f"{image_name}-build.yaml" + + os.makedirs(build_dir, exist_ok=True) + with open(build_file_path, "w") as f: + to_write = json.loads(build_config.model_dump_json()) + f.write(yaml.dump(to_write, sort_keys=False)) + + return_code = build_image( + build_config, build_file_path, image_name, template_name=template_name + ) + if return_code != 0: + return + + if template_name: + # copy run.yaml from template to build_dir instead of generating it again + template_path = ( + importlib.resources.files("llama_stack") + / f"templates/{template_name}/run.yaml" + ) + with importlib.resources.as_file(template_path) as path: + run_config_file = build_dir / f"{template_name}-run.yaml" + shutil.copy(path, run_config_file) + # Find all ${env.VARIABLE} patterns + cprint("Build Successful!", color="green") + else: + _generate_run_config(build_config, build_dir, image_name) + + +def _run_template_list_cmd() -> None: + # eventually, this should query a registry at llama.meta.com/llamastack/distributions + headers = [ + "Template Name", + # "Providers", + "Description", + ] + + rows = [] + for template_name, spec in available_templates_specs().items(): + rows.append( + [ + template_name, + # json.dumps(spec.distribution_spec.providers, indent=2), + spec.distribution_spec.description, + ] + ) + print_table( + rows, + headers, + separate_rows=True, + ) diff --git a/llama_stack/cli/stack/build.py b/llama_stack/cli/stack/build.py index 38994bebf..d00157710 100644 --- a/llama_stack/cli/stack/build.py +++ b/llama_stack/cli/stack/build.py @@ -4,38 +4,9 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. import argparse -import importlib.resources -import os -import shutil -from functools import lru_cache -from pathlib import Path -from typing import List, Optional +import textwrap from llama_stack.cli.subcommand import Subcommand -from llama_stack.distribution.datatypes import ( - BuildConfig, - DistributionSpec, - Provider, - StackRunConfig, -) -from llama_stack.distribution.distribution import get_provider_registry -from llama_stack.distribution.resolver import InvalidProviderError -from llama_stack.distribution.utils.dynamic import instantiate_class_type -from llama_stack.providers.datatypes import Api - -TEMPLATES_PATH = Path(__file__).parent.parent.parent / "templates" - - -@lru_cache() -def available_templates_specs() -> List[BuildConfig]: - import yaml - - template_specs = [] - for p in TEMPLATES_PATH.rglob("*build.yaml"): - with open(p, "r") as f: - build_config = BuildConfig(**yaml.safe_load(f)) - template_specs.append(build_config) - return template_specs class StackBuild(Subcommand): @@ -81,250 +52,21 @@ class StackBuild(Subcommand): default="conda", ) + self.parser.add_argument( + "--image-name", + type=str, + help=textwrap.dedent( + """[for image-type=conda] Name of the conda environment to use for the build. If +not specified, currently active Conda environment will be used. If no Conda +environment is active, you must specify a name. + """ + ), + default=None, + ) + def _run_stack_build_command(self, args: argparse.Namespace) -> None: - import textwrap + # always keep implementation completely silo-ed away from CLI so CLI + # can be fast to load and reduces dependencies + from ._build import run_stack_build_command - import yaml - from prompt_toolkit import prompt - from prompt_toolkit.completion import WordCompleter - from prompt_toolkit.validation import Validator - from termcolor import cprint - - from llama_stack.distribution.distribution import get_provider_registry - - if args.list_templates: - self._run_template_list_cmd(args) - return - - if args.template: - available_templates = available_templates_specs() - for build_config in available_templates: - if build_config.name == args.template: - if args.image_type: - build_config.image_type = args.image_type - else: - self.parser.error( - f"Please specify a image-type (docker | conda | venv) for {args.template}" - ) - self._run_stack_build_command_from_build_config( - build_config, - template_name=args.template, - ) - return - - self.parser.error( - f"Could not find template {args.template}. Please run `llama stack build --list-templates` to check out the available templates" - ) - return - - if not args.config and not args.template: - name = prompt( - "> Enter a name for your Llama Stack (e.g. my-local-stack): ", - validator=Validator.from_callable( - lambda x: len(x) > 0, - error_message="Name cannot be empty, please enter a name", - ), - ) - - image_type = prompt( - "> Enter the image type you want your Llama Stack to be built as (docker or conda or venv): ", - validator=Validator.from_callable( - lambda x: x in ["docker", "conda", "venv"], - error_message="Invalid image type, please enter conda or docker or venv", - ), - default="conda", - ) - - cprint( - textwrap.dedent( - """ - Llama Stack is composed of several APIs working together. Let's select - the provider types (implementations) you want to use for these APIs. - """, - ), - color="green", - ) - - print("Tip: use to see options for the providers.\n") - - providers = dict() - for api, providers_for_api in get_provider_registry().items(): - available_providers = [ - x - for x in providers_for_api.keys() - if x not in ("remote", "remote::sample") - ] - api_provider = prompt( - "> Enter provider for API {}: ".format(api.value), - completer=WordCompleter(available_providers), - complete_while_typing=True, - validator=Validator.from_callable( - lambda x: x in available_providers, - error_message="Invalid provider, use to see options", - ), - ) - - providers[api.value] = api_provider - - description = prompt( - "\n > (Optional) Enter a short description for your Llama Stack: ", - default="", - ) - - distribution_spec = DistributionSpec( - providers=providers, - description=description, - ) - - build_config = BuildConfig( - name=name, image_type=image_type, distribution_spec=distribution_spec - ) - self._run_stack_build_command_from_build_config(build_config) - return - - with open(args.config, "r") as f: - try: - build_config = BuildConfig(**yaml.safe_load(f)) - except Exception as e: - self.parser.error(f"Could not parse config file {args.config}: {e}") - return - self._run_stack_build_command_from_build_config(build_config) - - def _generate_run_config(self, build_config: BuildConfig, build_dir: Path) -> None: - """ - Generate a run.yaml template file for user to edit from a build.yaml file - """ - import json - - import yaml - from termcolor import cprint - - from llama_stack.distribution.build import ImageType - - apis = list(build_config.distribution_spec.providers.keys()) - run_config = StackRunConfig( - docker_image=( - build_config.name - if build_config.image_type == ImageType.docker.value - else None - ), - image_name=build_config.name, - conda_env=( - build_config.name - if build_config.image_type == ImageType.conda.value - else None - ), - apis=apis, - providers={}, - ) - # build providers dict - provider_registry = get_provider_registry() - for api in apis: - run_config.providers[api] = [] - provider_types = build_config.distribution_spec.providers[api] - if isinstance(provider_types, str): - provider_types = [provider_types] - - for i, provider_type in enumerate(provider_types): - pid = provider_type.split("::")[-1] - - p = provider_registry[Api(api)][provider_type] - if p.deprecation_error: - raise InvalidProviderError(p.deprecation_error) - - config_type = instantiate_class_type( - provider_registry[Api(api)][provider_type].config_class - ) - if hasattr(config_type, "sample_run_config"): - config = config_type.sample_run_config( - __distro_dir__=f"distributions/{build_config.name}" - ) - else: - config = {} - - p_spec = Provider( - provider_id=f"{pid}-{i}" if len(provider_types) > 1 else pid, - provider_type=provider_type, - config=config, - ) - run_config.providers[api].append(p_spec) - - os.makedirs(build_dir, exist_ok=True) - run_config_file = build_dir / f"{build_config.name}-run.yaml" - - with open(run_config_file, "w") as f: - to_write = json.loads(run_config.model_dump_json()) - f.write(yaml.dump(to_write, sort_keys=False)) - - cprint( - f"You can now edit {run_config_file} and run `llama stack run {run_config_file}`", - color="green", - ) - - def _run_stack_build_command_from_build_config( - self, - build_config: BuildConfig, - template_name: Optional[str] = None, - ) -> None: - import json - import os - - import yaml - from termcolor import cprint - - from llama_stack.distribution.build import build_image - from llama_stack.distribution.utils.config_dirs import DISTRIBS_BASE_DIR - - # save build.yaml spec for building same distribution again - build_dir = DISTRIBS_BASE_DIR / f"llamastack-{build_config.name}" - os.makedirs(build_dir, exist_ok=True) - build_file_path = build_dir / f"{build_config.name}-build.yaml" - - with open(build_file_path, "w") as f: - to_write = json.loads(build_config.model_dump_json()) - f.write(yaml.dump(to_write, sort_keys=False)) - - return_code = build_image(build_config, build_file_path) - if return_code != 0: - return - - if template_name: - # copy run.yaml from template to build_dir instead of generating it again - template_path = ( - importlib.resources.files("llama_stack") - / f"templates/{template_name}/run.yaml" - ) - with importlib.resources.as_file(template_path) as path: - run_config_file = build_dir / f"{build_config.name}-run.yaml" - shutil.copy(path, run_config_file) - # Find all ${env.VARIABLE} patterns - cprint("Build Successful!", color="green") - else: - self._generate_run_config(build_config, build_dir) - - def _run_template_list_cmd(self, args: argparse.Namespace) -> None: - import json - - from llama_stack.cli.table import print_table - - # eventually, this should query a registry at llama.meta.com/llamastack/distributions - headers = [ - "Template Name", - "Providers", - "Description", - ] - - rows = [] - for spec in available_templates_specs(): - rows.append( - [ - spec.name, - json.dumps(spec.distribution_spec.providers, indent=2), - spec.distribution_spec.description, - ] - ) - print_table( - rows, - headers, - separate_rows=True, - ) + return run_stack_build_command(self.parser, args) diff --git a/llama_stack/cli/stack/run.py b/llama_stack/cli/stack/run.py index 90b2ecf6d..7942f603a 100644 --- a/llama_stack/cli/stack/run.py +++ b/llama_stack/cli/stack/run.py @@ -37,6 +37,11 @@ class StackRun(Subcommand): help="Port to run the server on. Defaults to 5000", default=int(os.getenv("LLAMA_STACK_PORT", 5000)), ) + self.parser.add_argument( + "--image-name", + type=str, + help="Name of the image to run. Defaults to the current conda environment", + ) self.parser.add_argument( "--disable-ipv6", action="store_true", @@ -53,8 +58,11 @@ class StackRun(Subcommand): def _run_stack_run_cmd(self, args: argparse.Namespace) -> None: import importlib.resources + import json + import subprocess import yaml + from termcolor import cprint from llama_stack.distribution.build import ImageType from llama_stack.distribution.configure import parse_and_maybe_upgrade_config @@ -99,11 +107,11 @@ class StackRun(Subcommand): if not config_file.exists(): self.parser.error( - f"File {str(config_file)} does not exist. Please run `llama stack build` to generate (and optionally edit) a run.yaml file" + f"File {str(config_file)} does not exist.\n\nPlease run `llama stack build` to generate (and optionally edit) a run.yaml file" ) return - print(f"Using config file: {config_file}") + print(f"Using run configuration: {config_file}") config_dict = yaml.safe_load(config_file.read_text()) config = parse_and_maybe_upgrade_config(config_dict) @@ -114,13 +122,52 @@ class StackRun(Subcommand): ) run_args = [script, config.docker_image] else: + current_conda_env = os.environ.get("CONDA_DEFAULT_ENV") + image_name = args.image_name or current_conda_env + if not image_name: + cprint( + "No current conda environment detected, please specify a conda environment name with --image-name", + color="red", + ) + return + + def get_conda_prefix(env_name): + # Get conda environments info + conda_env_info = json.loads( + subprocess.check_output( + ["conda", "info", "--envs", "--json"] + ).decode() + ) + envs = conda_env_info["envs"] + for envpath in envs: + if envpath.endswith(env_name): + return envpath + return None + + print(f"Using conda environment: {image_name}") + conda_prefix = get_conda_prefix(image_name) + if not conda_prefix: + cprint( + f"Conda environment {image_name} does not exist.", + color="red", + ) + return + + build_file = Path(conda_prefix) / "llamastack-build.yaml" + if not build_file.exists(): + cprint( + f"Build file {build_file} does not exist.\n\nPlease run `llama stack build` or specify the correct conda environment name with --image-name", + color="red", + ) + return + script = ( importlib.resources.files("llama_stack") / "distribution/start_conda_env.sh" ) run_args = [ script, - config.conda_env, + image_name, ] run_args.extend([str(config_file), str(args.port)]) @@ -129,13 +176,17 @@ class StackRun(Subcommand): for env_var in args.env: if "=" not in env_var: - self.parser.error( - f"Environment variable '{env_var}' must be in KEY=VALUE format" + cprint( + f"Environment variable '{env_var}' must be in KEY=VALUE format", + color="red", ) return key, value = env_var.split("=", 1) # split on first = only if not key: - self.parser.error(f"Environment variable '{env_var}' has empty key") + cprint( + f"Environment variable '{env_var}' has empty key", + color="red", + ) return run_args.extend(["--env", f"{key}={value}"]) diff --git a/llama_stack/distribution/build.py b/llama_stack/distribution/build.py index a8b2342af..b8b4188ac 100644 --- a/llama_stack/distribution/build.py +++ b/llama_stack/distribution/build.py @@ -10,7 +10,7 @@ import sys from enum import Enum from pathlib import Path -from typing import Dict, List +from typing import Dict, List, Optional from pydantic import BaseModel from termcolor import cprint @@ -106,6 +106,8 @@ def print_pip_install_help(providers: Dict[str, List[Provider]]): def build_image( build_config: BuildConfig, build_file_path: Path, + image_name: str, + template_name: Optional[str] = None, ): docker_image = build_config.distribution_spec.docker_image or "python:3.10-slim" @@ -115,32 +117,34 @@ def build_image( normal_deps += SERVER_DEPENDENCIES if build_config.image_type == ImageType.docker.value: - script = ( + script = str( importlib.resources.files("llama_stack") / "distribution/build_container.sh" ) args = [ script, - build_config.name, + image_name, docker_image, str(build_file_path), str(BUILDS_BASE_DIR / ImageType.docker.value), " ".join(normal_deps), ] elif build_config.image_type == ImageType.conda.value: - script = ( + script = str( importlib.resources.files("llama_stack") / "distribution/build_conda_env.sh" ) args = [ script, - build_config.name, + str(image_name), str(build_file_path), " ".join(normal_deps), ] elif build_config.image_type == ImageType.venv.value: - script = importlib.resources.files("llama_stack") / "distribution/build_venv.sh" + script = str( + importlib.resources.files("llama_stack") / "distribution/build_venv.sh" + ) args = [ script, - build_config.name, + str(image_name), str(build_file_path), " ".join(normal_deps), ] @@ -156,7 +160,7 @@ def build_image( if return_code != 0: log.error( - f"Failed to build target {build_config.name} with return code {return_code}", + f"Failed to build target {image_name} with return code {return_code}", ) return return_code diff --git a/llama_stack/distribution/build_conda_env.sh b/llama_stack/distribution/build_conda_env.sh index 461f27baa..606fbf19d 100755 --- a/llama_stack/distribution/build_conda_env.sh +++ b/llama_stack/distribution/build_conda_env.sh @@ -18,8 +18,8 @@ if [ -n "$LLAMA_MODELS_DIR" ]; then fi if [ "$#" -lt 3 ]; then - echo "Usage: $0 []" >&2 - echo "Example: $0 mybuild ./my-stack-build.yaml 'numpy pandas scipy'" >&2 + echo "Usage: $0 []" >&2 + echo "Example: $0 my-conda-env ./my-stack-build.yaml 'numpy pandas scipy'" >&2 exit 1 fi @@ -27,8 +27,7 @@ special_pip_deps="$4" set -euo pipefail -build_name="$1" -env_name="llamastack-$build_name" +env_name="$1" build_file_path="$2" pip_dependencies="$3" @@ -137,8 +136,8 @@ ensure_conda_env_python310() { fi fi - mv $build_file_path $CONDA_PREFIX/ - echo "Build spec configuration saved at $CONDA_PREFIX/$build_name-build.yaml" + mv $build_file_path $CONDA_PREFIX/llamastack-build.yaml + echo "Build spec configuration saved at $CONDA_PREFIX/llamastack-build.yaml" } ensure_conda_env_python310 "$env_name" "$pip_dependencies" "$special_pip_deps" diff --git a/llama_stack/distribution/datatypes.py b/llama_stack/distribution/datatypes.py index d0ccd6cd1..0a293cbc2 100644 --- a/llama_stack/distribution/datatypes.py +++ b/llama_stack/distribution/datatypes.py @@ -131,10 +131,6 @@ this could be just a hash default=None, description="Reference to the docker image if this package refers to a container", ) - conda_env: Optional[str] = Field( - default=None, - description="Reference to the conda environment if this package refers to a conda environment", - ) apis: List[str] = Field( default_factory=list, description=""" @@ -166,7 +162,7 @@ a default SQLite store will be used.""", class BuildConfig(BaseModel): version: str = LLAMA_STACK_BUILD_CONFIG_VERSION - name: str + distribution_spec: DistributionSpec = Field( description="The distribution spec to build including API providers. " ) diff --git a/llama_stack/distribution/start_conda_env.sh b/llama_stack/distribution/start_conda_env.sh index f478a8bd8..c37f30ef0 100755 --- a/llama_stack/distribution/start_conda_env.sh +++ b/llama_stack/distribution/start_conda_env.sh @@ -23,8 +23,7 @@ if [ $# -lt 3 ]; then exit 1 fi -build_name="$1" -env_name="llamastack-$build_name" +env_name="$1" shift yaml_config="$1" diff --git a/llama_stack/providers/inline/inference/meta_reference/quantization/scripts/build_conda.sh b/llama_stack/providers/inline/inference/meta_reference/quantization/scripts/build_conda.sh deleted file mode 100644 index ae0ed0bac..000000000 --- a/llama_stack/providers/inline/inference/meta_reference/quantization/scripts/build_conda.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/bin/bash - -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the terms described in the LICENSE file in -# the root directory of this source tree. - -if [[ $# -ne 1 ]]; then - echo "Error: Please provide the name of CONDA environment you wish to create" - exit 1 -fi - -ENV_NAME=$1 - -set -eu -eval "$(conda shell.bash hook)" - -echo "Will build env (or overwrite) named '$ENV_NAME'" - -set -x - -run_build() { - # Set up the conda environment - yes | conda remove --name $ENV_NAME --all - yes | conda create -n $ENV_NAME python=3.10 - conda activate $ENV_NAME - - # PT nightly - pip install --pre torch --index-url https://download.pytorch.org/whl/nightly/cu121 - - # install dependencies for `llama-agentic-system` - pip install -r fp8_requirements.txt -} - -run_build From 12c994b5b2c1e76b24b3646bceee38e27a9ca19a Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 16 Jan 2025 13:47:08 -0800 Subject: [PATCH 034/107] REST API fixes (#789) # What does this PR do? Client SDK fixes ## Test Plan LLAMA_STACK_CONFIG="/Users/dineshyv/.llama/distributions/llamastack-fireworks/fireworks-run.yaml" pytest -v tests/client-sdk/safety/test_safety.py LLAMA_STACK_CONFIG="/Users/dineshyv/.llama/distributions/llamastack-fireworks/fireworks-run.yaml" pytest -v tests/client-sdk/memory/test_memory.py --- docs/resources/llama-stack-spec.html | 4 +++ docs/resources/llama-stack-spec.yaml | 3 ++ llama_stack/apis/inspect/inspect.py | 1 + llama_stack/distribution/inspect.py | 35 +++++++++++--------- tests/client-sdk/agents/test_agents.py | 4 +-- tests/client-sdk/inference/test_inference.py | 11 +++--- tests/client-sdk/memory/test_memory.py | 27 ++++++--------- tests/client-sdk/safety/test_safety.py | 9 +++-- 8 files changed, 51 insertions(+), 43 deletions(-) diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index 750dce798..38cabdd3e 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -7196,6 +7196,9 @@ "ProviderInfo": { "type": "object", "properties": { + "api": { + "type": "string" + }, "provider_id": { "type": "string" }, @@ -7205,6 +7208,7 @@ }, "additionalProperties": false, "required": [ + "api", "provider_id", "provider_type" ] diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index 9d5f9cd60..75bc25e94 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -1678,11 +1678,14 @@ components: ProviderInfo: additionalProperties: false properties: + api: + type: string provider_id: type: string provider_type: type: string required: + - api - provider_id - provider_type type: object diff --git a/llama_stack/apis/inspect/inspect.py b/llama_stack/apis/inspect/inspect.py index 9d20c27b3..cd51469c1 100644 --- a/llama_stack/apis/inspect/inspect.py +++ b/llama_stack/apis/inspect/inspect.py @@ -12,6 +12,7 @@ from pydantic import BaseModel @json_schema_type class ProviderInfo(BaseModel): + api: str provider_id: str provider_type: str diff --git a/llama_stack/distribution/inspect.py b/llama_stack/distribution/inspect.py index 08dfb329e..b7ee4a219 100644 --- a/llama_stack/distribution/inspect.py +++ b/llama_stack/distribution/inspect.py @@ -44,15 +44,18 @@ class DistributionInspectImpl(Inspect): ret = [] for api, providers in run_config.providers.items(): - ret.append( - ProviderInfo( - provider_id=p.provider_id, - provider_type=p.provider_type, - ) - for p in providers + ret.extend( + [ + ProviderInfo( + api=api, + provider_id=p.provider_id, + provider_type=p.provider_type, + ) + for p in providers + ] ) - return ret + return ListProvidersResponse(data=ret) async def list_routes(self) -> ListRoutesResponse: run_config = self.config.run_config @@ -61,16 +64,18 @@ class DistributionInspectImpl(Inspect): all_endpoints = get_all_api_endpoints() for api, endpoints in all_endpoints.items(): providers = run_config.providers.get(api.value, []) - ret.append( - RouteInfo( - route=e.route, - method=e.method, - provider_types=[p.provider_type for p in providers], - ) - for e in endpoints + ret.extend( + [ + RouteInfo( + route=e.route, + method=e.method, + provider_types=[p.provider_type for p in providers], + ) + for e in endpoints + ] ) - return ret + return ListRoutesResponse(data=ret) async def health(self) -> HealthInfo: return HealthInfo(status="OK") diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py index 747b64dd1..19a4064a0 100644 --- a/tests/client-sdk/agents/test_agents.py +++ b/tests/client-sdk/agents/test_agents.py @@ -83,13 +83,13 @@ class TestClientTool(ClientTool): def agent_config(llama_stack_client): available_models = [ model.identifier - for model in llama_stack_client.models.list().data + for model in llama_stack_client.models.list() if model.identifier.startswith("meta-llama") and "405" not in model.identifier ] model_id = available_models[0] print(f"Using model: {model_id}") available_shields = [ - shield.identifier for shield in llama_stack_client.shields.list().data + shield.identifier for shield in llama_stack_client.shields.list() ] available_shields = available_shields[:1] print(f"Using shield: {available_shields}") diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py index 5191a3f7f..671a37926 100644 --- a/tests/client-sdk/inference/test_inference.py +++ b/tests/client-sdk/inference/test_inference.py @@ -5,10 +5,8 @@ # the root directory of this source tree. import pytest - from pydantic import BaseModel - PROVIDER_TOOL_PROMPT_FORMAT = { "remote::ollama": "python_list", "remote::together": "json", @@ -28,15 +26,16 @@ def provider_tool_format(inference_provider_type): @pytest.fixture(scope="session") def inference_provider_type(llama_stack_client): providers = llama_stack_client.providers.list() - assert len(providers.inference) > 0 - return providers.inference[0]["provider_type"] + inference_providers = [p for p in providers if p.api == "inference"] + assert len(inference_providers) > 0, "No inference providers found" + return inference_providers[0].provider_type @pytest.fixture(scope="session") def text_model_id(llama_stack_client): available_models = [ model.identifier - for model in llama_stack_client.models.list().data + for model in llama_stack_client.models.list() if model.identifier.startswith("meta-llama") and "405" not in model.identifier ] assert len(available_models) > 0 @@ -47,7 +46,7 @@ def text_model_id(llama_stack_client): def vision_model_id(llama_stack_client): available_models = [ model.identifier - for model in llama_stack_client.models.list().data + for model in llama_stack_client.models.list() if "vision" in model.identifier.lower() ] if len(available_models) == 0: diff --git a/tests/client-sdk/memory/test_memory.py b/tests/client-sdk/memory/test_memory.py index a5f154fda..1e9b34355 100644 --- a/tests/client-sdk/memory/test_memory.py +++ b/tests/client-sdk/memory/test_memory.py @@ -7,16 +7,15 @@ import random import pytest -from llama_stack.apis.memory import MemoryBankDocument +from llama_stack.apis.memory import MemoryBankDocument from llama_stack_client.types.memory_insert_params import Document @pytest.fixture(scope="function") def empty_memory_bank_registry(llama_stack_client): memory_banks = [ - memory_bank.identifier - for memory_bank in llama_stack_client.memory_banks.list().data + memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() ] for memory_bank_id in memory_banks: llama_stack_client.memory_banks.unregister(memory_bank_id=memory_bank_id) @@ -36,8 +35,7 @@ def single_entry_memory_bank_registry(llama_stack_client, empty_memory_bank_regi provider_id="faiss", ) memory_banks = [ - memory_bank.identifier - for memory_bank in llama_stack_client.memory_banks.list().data + memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() ] return memory_banks @@ -106,8 +104,7 @@ def test_memory_bank_retrieve(llama_stack_client, empty_memory_bank_registry): def test_memory_bank_list(llama_stack_client, empty_memory_bank_registry): memory_banks_after_register = [ - memory_bank.identifier - for memory_bank in llama_stack_client.memory_banks.list().data + memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() ] assert len(memory_banks_after_register) == 0 @@ -127,16 +124,14 @@ def test_memory_bank_register(llama_stack_client, empty_memory_bank_registry): ) memory_banks_after_register = [ - memory_bank.identifier - for memory_bank in llama_stack_client.memory_banks.list().data + memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() ] assert memory_banks_after_register == [memory_bank_id] def test_memory_bank_unregister(llama_stack_client, single_entry_memory_bank_registry): memory_banks = [ - memory_bank.identifier - for memory_bank in llama_stack_client.memory_banks.list().data + memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() ] assert len(memory_banks) == 1 @@ -144,8 +139,7 @@ def test_memory_bank_unregister(llama_stack_client, single_entry_memory_bank_reg llama_stack_client.memory_banks.unregister(memory_bank_id=memory_bank_id) memory_banks = [ - memory_bank.identifier - for memory_bank in llama_stack_client.memory_banks.list().data + memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() ] assert len(memory_banks) == 0 @@ -201,10 +195,10 @@ def test_memory_bank_insert_inline_and_query( def test_memory_bank_insert_from_url_and_query( llama_stack_client, empty_memory_bank_registry ): - providers = llama_stack_client.providers.list().memory + providers = [p for p in llama_stack_client.providers.list() if p.api == "memory"] assert len(providers) > 0 - memory_provider_id = providers[0]["provider_id"] + memory_provider_id = providers[0].provider_id memory_bank_id = "test_bank" llama_stack_client.memory_banks.register( @@ -220,8 +214,7 @@ def test_memory_bank_insert_from_url_and_query( # list to check memory bank is successfully registered available_memory_banks = [ - memory_bank.identifier - for memory_bank in llama_stack_client.memory_banks.list().data + memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() ] assert memory_bank_id in available_memory_banks diff --git a/tests/client-sdk/safety/test_safety.py b/tests/client-sdk/safety/test_safety.py index 2d79bda5e..6af417a09 100644 --- a/tests/client-sdk/safety/test_safety.py +++ b/tests/client-sdk/safety/test_safety.py @@ -11,7 +11,6 @@ import pytest from llama_stack.apis.safety import ViolationLevel - VISION_SHIELD_ENABLED_PROVIDERS = {"together"} CODE_SCANNER_ENABLED_PROVIDERS = {"ollama", "together", "fireworks"} @@ -30,7 +29,7 @@ def data_url_from_image(file_path): @pytest.fixture(scope="session") def available_shields(llama_stack_client): - return [shield.identifier for shield in llama_stack_client.shields.list().data] + return [shield.identifier for shield in llama_stack_client.shields.list()] @pytest.fixture(scope="session") @@ -54,7 +53,11 @@ def code_scanner_shield_id(available_shields): @pytest.fixture(scope="session") def model_providers(llama_stack_client): return set( - [x["provider_id"] for x in llama_stack_client.providers.list().inference] + [ + x.provider_id + for x in llama_stack_client.providers.list() + if x.api == "inference" + ] ) From a6b9f2cec7588fc2eaeb8f1e2bddd7701eb163c0 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Thu, 16 Jan 2025 13:53:06 -0800 Subject: [PATCH 035/107] fix cerebras template (#790) # What does this PR do? - fix cerebras template ## Test Plan ``` llama stack build --template cerebras --image-type conda llama stack run cerebras LLAMA_STACK_BASE_URL="http://localhost:5000" pytest -v tests/client-sdk/ --html=report.html --self-contained-html ``` ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- distributions/dependencies.json | 3 +++ .../self_hosted_distro/cerebras.md | 3 +++ llama_stack/templates/cerebras/build.yaml | 9 +++++++ llama_stack/templates/cerebras/cerebras.py | 3 +++ llama_stack/templates/cerebras/run.yaml | 25 +++++++++++++++++++ 5 files changed, 43 insertions(+) diff --git a/distributions/dependencies.json b/distributions/dependencies.json index f36b35292..ab3a367f1 100644 --- a/distributions/dependencies.json +++ b/distributions/dependencies.json @@ -342,9 +342,11 @@ ], "cerebras": [ "aiosqlite", + "autoevals", "blobfile", "cerebras_cloud_sdk", "chardet", + "datasets", "faiss-cpu", "fastapi", "fire", @@ -352,6 +354,7 @@ "matplotlib", "nltk", "numpy", + "openai", "opentelemetry-exporter-otlp-proto-http", "opentelemetry-sdk", "pandas", diff --git a/docs/source/distributions/self_hosted_distro/cerebras.md b/docs/source/distributions/self_hosted_distro/cerebras.md index be69c8f92..302d121dd 100644 --- a/docs/source/distributions/self_hosted_distro/cerebras.md +++ b/docs/source/distributions/self_hosted_distro/cerebras.md @@ -5,9 +5,12 @@ The `llamastack/distribution-cerebras` distribution consists of the following pr | API | Provider(s) | |-----|-------------| | agents | `inline::meta-reference` | +| datasetio | `remote::huggingface`, `inline::localfs` | +| eval | `inline::meta-reference` | | inference | `remote::cerebras` | | memory | `inline::meta-reference` | | safety | `inline::llama-guard` | +| scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` | diff --git a/llama_stack/templates/cerebras/build.yaml b/llama_stack/templates/cerebras/build.yaml index 307e0303a..0fe568d09 100644 --- a/llama_stack/templates/cerebras/build.yaml +++ b/llama_stack/templates/cerebras/build.yaml @@ -11,6 +11,15 @@ distribution_spec: - inline::meta-reference agents: - inline::meta-reference + eval: + - inline::meta-reference + datasetio: + - remote::huggingface + - inline::localfs + scoring: + - inline::basic + - inline::llm-as-judge + - inline::braintrust telemetry: - inline::meta-reference tool_runtime: diff --git a/llama_stack/templates/cerebras/cerebras.py b/llama_stack/templates/cerebras/cerebras.py index b51617f35..6571170dd 100644 --- a/llama_stack/templates/cerebras/cerebras.py +++ b/llama_stack/templates/cerebras/cerebras.py @@ -29,6 +29,9 @@ def get_distribution_template() -> DistributionTemplate: "safety": ["inline::llama-guard"], "memory": ["inline::meta-reference"], "agents": ["inline::meta-reference"], + "eval": ["inline::meta-reference"], + "datasetio": ["remote::huggingface", "inline::localfs"], + "scoring": ["inline::basic", "inline::llm-as-judge", "inline::braintrust"], "telemetry": ["inline::meta-reference"], "tool_runtime": [ "remote::brave-search", diff --git a/llama_stack/templates/cerebras/run.yaml b/llama_stack/templates/cerebras/run.yaml index e06b17a50..42146ad4b 100644 --- a/llama_stack/templates/cerebras/run.yaml +++ b/llama_stack/templates/cerebras/run.yaml @@ -3,9 +3,12 @@ image_name: cerebras conda_env: cerebras apis: - agents +- datasetio +- eval - inference - memory - safety +- scoring - telemetry - tool_runtime providers: @@ -38,6 +41,28 @@ providers: type: sqlite namespace: null db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/cerebras}/agents_store.db + eval: + - provider_id: meta-reference + provider_type: inline::meta-reference + config: {} + datasetio: + - provider_id: huggingface + provider_type: remote::huggingface + config: {} + - provider_id: localfs + provider_type: inline::localfs + config: {} + scoring: + - provider_id: basic + provider_type: inline::basic + config: {} + - provider_id: llm-as-judge + provider_type: inline::llm-as-judge + config: {} + - provider_id: braintrust + provider_type: inline::braintrust + config: + openai_api_key: ${env.OPENAI_API_KEY:} telemetry: - provider_id: meta-reference provider_type: inline::meta-reference From fcd1a57429fa99625b2abf73b8fe9b295bea64b9 Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 16 Jan 2025 14:00:48 -0800 Subject: [PATCH 036/107] update notebook --- ...Llama_Stack_Building_AI_Applications.ipynb | 639 +++++++----------- 1 file changed, 248 insertions(+), 391 deletions(-) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index d91a7bea3..e5c1a4dc1 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -589,7 +589,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "id": "E1UFuJC570Tk", "metadata": { "colab": { @@ -728,175 +728,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "Removed handler StreamHandler from root logger\n" + "Not in Google Colab environment\n", + "\u001b[33mWarning: `bwrap` is not available. Code interpreter tool will not work correctly.\u001b[0m\n" ] }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/usr/local/lib/python3.10/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning: \n", - "The secret `HF_TOKEN` does not exist in your Colab secrets.\n", - "To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session.\n", - "You will be able to reuse this secret in all of your notebooks.\n", - "Please note that authentication is recommended but still optional to access public models or datasets.\n", - " warnings.warn(\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "88f0c88612bb45d59f07e93567cc0e14", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "modules.json: 0%| | 0.00/349 [00:00[]\n", "metadata_store:\n", - " db_path: /root/.llama/distributions/together/registry.db\n", + " db_path: /Users/dineshyv/.llama/distributions/together/registry.db\n", " namespace: null\n", " type: sqlite\n", "models:\n", @@ -999,7 +834,7 @@ " agents:\n", " - config:\n", " persistence_store:\n", - " db_path: /root/.llama/distributions/together/agents_store.db\n", + " db_path: /Users/dineshyv/.llama/distributions/together/agents_store.db\n", " namespace: null\n", " type: sqlite\n", " provider_id: meta-reference\n", @@ -1027,7 +862,7 @@ " memory:\n", " - config:\n", " kvstore:\n", - " db_path: /root/.llama/distributions/together/faiss_store.db\n", + " db_path: /Users/dineshyv/.llama/distributions/together/faiss_store.db\n", " namespace: null\n", " type: sqlite\n", " provider_id: faiss\n", @@ -1051,7 +886,7 @@ " - config:\n", " service_name: llama-stack\n", " sinks: sqlite\n", - " sqlite_db_path: /root/.llama/distributions/together/trace_store.db\n", + " sqlite_db_path: /Users/dineshyv/.llama/distributions/together/trace_store.db\n", " provider_id: meta-reference\n", " provider_type: inline::meta-reference\n", " tool_runtime:\n", @@ -1112,7 +947,7 @@ "image_name: together\n", "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "metadata_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", + " db_path: \u001b[35m/Users/dineshyv/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", "models:\n", @@ -1181,7 +1016,7 @@ " agents:\n", " - config:\n", " persistence_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", + " db_path: \u001b[35m/Users/dineshyv/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", " provider_id: meta-reference\n", @@ -1209,7 +1044,7 @@ " memory:\n", " - config:\n", " kvstore:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", + " db_path: \u001b[35m/Users/dineshyv/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", " provider_id: faiss\n", @@ -1233,7 +1068,7 @@ " - config:\n", " service_name: llama-stack\n", " sinks: sqlite\n", - " sqlite_db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", + " sqlite_db_path: \u001b[35m/Users/dineshyv/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", " provider_id: meta-reference\n", " provider_type: inline::meta-reference\n", " tool_runtime:\n", @@ -1320,7 +1155,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 21, "id": "ruO9jQna_t_S", "metadata": { "colab": { @@ -1336,16 +1171,16 @@ "output_type": "stream", "text": [ "Available models:\n", - "meta-llama/Llama-3.1-8B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo) \n", - "meta-llama/Llama-3.1-70B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo) \n", + "all-MiniLM-L6-v2 (provider's alias: all-MiniLM-L6-v2) \n", "meta-llama/Llama-3.1-405B-Instruct-FP8 (provider's alias: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo) \n", - "meta-llama/Llama-3.2-3B-Instruct (provider's alias: meta-llama/Llama-3.2-3B-Instruct-Turbo) \n", + "meta-llama/Llama-3.1-70B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo) \n", + "meta-llama/Llama-3.1-8B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo) \n", "meta-llama/Llama-3.2-11B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo) \n", + "meta-llama/Llama-3.2-3B-Instruct (provider's alias: meta-llama/Llama-3.2-3B-Instruct-Turbo) \n", "meta-llama/Llama-3.2-90B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo) \n", "meta-llama/Llama-3.3-70B-Instruct (provider's alias: meta-llama/Llama-3.3-70B-Instruct-Turbo) \n", - "meta-llama/Llama-Guard-3-8B (provider's alias: meta-llama/Meta-Llama-Guard-3-8B) \n", "meta-llama/Llama-Guard-3-11B-Vision (provider's alias: meta-llama/Llama-Guard-3-11B-Vision-Turbo) \n", - "all-MiniLM-L6-v2 (provider's alias: all-MiniLM-L6-v2) \n", + "meta-llama/Llama-Guard-3-8B (provider's alias: meta-llama/Meta-Llama-Guard-3-8B) \n", "----\n", "Available shields (safety models):\n", "meta-llama/Llama-Guard-3-8B\n", @@ -1357,14 +1192,12 @@ "from rich.pretty import pprint\n", "\n", "print(\"Available models:\")\n", - "response = client.models.list()\n", - "for m in response.data:\n", + "for m in client.models.list():\n", " print(f\"{m.identifier} (provider's alias: {m.provider_resource_id}) \")\n", "\n", "print(\"----\")\n", "print(\"Available shields (safety models):\")\n", - "response = client.shields.list()\n", - "for s in response.data:\n", + "for s in client.shields.list():\n", " print(s.identifier)\n", "print(\"----\")\n" ] @@ -1383,7 +1216,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "LINBvv8lwTJh", "metadata": { "colab": { @@ -1396,14 +1229,11 @@ "outputs": [ { "data": { - "application/vnd.google.colaboratory.intrinsic+json": { - "type": "string" - }, "text/plain": [ "'meta-llama/Llama-3.1-70B-Instruct'" ] }, - "execution_count": 5, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -1428,7 +1258,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "77c29dba", "metadata": { "colab": { @@ -1442,10 +1272,10 @@ "name": "stdout", "output_type": "stream", "text": [ - "Here's a short poem about a llama:\n", + "Here's a two-sentence poem about a llama:\n", "\n", - "In the Andes, a llama does roam,\n", - "With soft fur and eyes that are gentle at home.\n" + "With gentle eyes and a soft, fuzzy face,\n", + "The llama roams, a peaceful, gentle pace.\n" ] } ], @@ -1604,7 +1434,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "id": "d119026e", "metadata": { "colab": { @@ -1619,23 +1449,23 @@ "output_type": "stream", "text": [ "User> Write me a sonnet about llama green\n", - "Assistant> Amidst the Andes' windswept, rugged land,\n", - "A creature roams with gentle, watchful eyes,\n", - "The llama, soft and quiet, takes its stand,\n", - "Its fleece a warm and vibrant, wavy guise.\n", + "\u001b[36mAssistant> \u001b[0m\u001b[33mIn\u001b[0m\u001b[33m And\u001b[0m\u001b[33mean\u001b[0m\u001b[33m high\u001b[0m\u001b[33mlands\u001b[0m\u001b[33m,\u001b[0m\u001b[33m where\u001b[0m\u001b[33m the\u001b[0m\u001b[33m air\u001b[0m\u001b[33m is\u001b[0m\u001b[33m thin\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mA\u001b[0m\u001b[33m gentle\u001b[0m\u001b[33m creature\u001b[0m\u001b[33m ro\u001b[0m\u001b[33mams\u001b[0m\u001b[33m,\u001b[0m\u001b[33m with\u001b[0m\u001b[33m steps\u001b[0m\u001b[33m serene\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mThe\u001b[0m\u001b[33m llama\u001b[0m\u001b[33m,\u001b[0m\u001b[33m with\u001b[0m\u001b[33m its\u001b[0m\u001b[33m soft\u001b[0m\u001b[33m and\u001b[0m\u001b[33m wool\u001b[0m\u001b[33mly\u001b[0m\u001b[33m skin\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mA\u001b[0m\u001b[33m symbol\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m region\u001b[0m\u001b[33m's\u001b[0m\u001b[33m myst\u001b[0m\u001b[33mic\u001b[0m\u001b[33m she\u001b[0m\u001b[33men\u001b[0m\u001b[33m.\n", "\n", - "Its ears, so delicate and finely tuned,\n", - "Catch every sound that whispers through the air,\n", - "Its steps, a soft and careful, measured pace,\n", - "A steadfast friend, with loyalty to share.\n", + "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m eyes\u001b[0m\u001b[33m,\u001b[0m\u001b[33m like\u001b[0m\u001b[33m pools\u001b[0m\u001b[33m of\u001b[0m\u001b[33m calm\u001b[0m\u001b[33m and\u001b[0m\u001b[33m peaceful\u001b[0m\u001b[33m night\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mReflect\u001b[0m\u001b[33m the\u001b[0m\u001b[33m beauty\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m mountain\u001b[0m\u001b[33m's\u001b[0m\u001b[33m might\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m ears\u001b[0m\u001b[33m,\u001b[0m\u001b[33m a\u001b[0m\u001b[33m-t\u001b[0m\u001b[33mwitch\u001b[0m\u001b[33m with\u001b[0m\u001b[33m every\u001b[0m\u001b[33m sound\u001b[0m\u001b[33m and\u001b[0m\u001b[33m sight\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mAs\u001b[0m\u001b[33m if\u001b[0m\u001b[33m it\u001b[0m\u001b[33m listens\u001b[0m\u001b[33m to\u001b[0m\u001b[33m the\u001b[0m\u001b[33m wind\u001b[0m\u001b[33m's\u001b[0m\u001b[33m soft\u001b[0m\u001b[33m light\u001b[0m\u001b[33m.\n", "\n", - "Its face, a vision of calm serenity,\n", - "Untroubled by the world's wild stormy tides,\n", - "The llama's heart beats strong with quiet peace,\n", - "A reflection of its steadfast, gentle pride.\n", + "\u001b[0m\u001b[33mWith\u001b[0m\u001b[33m steps\u001b[0m\u001b[33m that\u001b[0m\u001b[33m glide\u001b[0m\u001b[33m,\u001b[0m\u001b[33m like\u001b[0m\u001b[33m a\u001b[0m\u001b[33m slow\u001b[0m\u001b[33m-moving\u001b[0m\u001b[33m stream\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mIt\u001b[0m\u001b[33m navig\u001b[0m\u001b[33mates\u001b[0m\u001b[33m the\u001b[0m\u001b[33m rocky\u001b[0m\u001b[33m,\u001b[0m\u001b[33m winding\u001b[0m\u001b[33m dream\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m soft\u001b[0m\u001b[33m hum\u001b[0m\u001b[33m,\u001b[0m\u001b[33m a\u001b[0m\u001b[33m soothing\u001b[0m\u001b[33m melody\u001b[0m\u001b[33m,\u001b[0m\u001b[33m it\u001b[0m\u001b[33m seems\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mA\u001b[0m\u001b[33m l\u001b[0m\u001b[33mull\u001b[0m\u001b[33maby\u001b[0m\u001b[33m that\u001b[0m\u001b[33m cal\u001b[0m\u001b[33mms\u001b[0m\u001b[33m the\u001b[0m\u001b[33m heart\u001b[0m\u001b[33m's\u001b[0m\u001b[33m wild\u001b[0m\u001b[33m theme\u001b[0m\u001b[33m.\n", "\n", - "And when it speaks, its soft and soothing voice,\n", - "Echoes whispers of a gentle, loving choice.\n" + "\u001b[0m\u001b[33mAnd\u001b[0m\u001b[33m as\u001b[0m\u001b[33m it\u001b[0m\u001b[33m walks\u001b[0m\u001b[33m,\u001b[0m\u001b[33m its\u001b[0m\u001b[33m beauty\u001b[0m\u001b[33m we\u001b[0m\u001b[33m behold\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mA\u001b[0m\u001b[33m treasure\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m And\u001b[0m\u001b[33mes\u001b[0m\u001b[33m,\u001b[0m\u001b[33m young\u001b[0m\u001b[33m and\u001b[0m\u001b[33m old\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n" ] } ], @@ -1670,7 +1500,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "id": "axdQIRaJCYAV", "metadata": { "colab": { @@ -1685,7 +1515,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "/usr/local/lib/python3.10/dist-packages/pydantic/main.py:426: UserWarning: Pydantic serializer warnings:\n", + "/Users/dineshyv/miniconda3/envs/stack/lib/python3.10/site-packages/pydantic/main.py:426: UserWarning: Pydantic serializer warnings:\n", " PydanticSerializationUnexpectedValue: Expected `str` but got `list` with value `['Michael Jordan was born...ut\", \"type\": \"object\"}']` - serialized value may not be as expected\n", " PydanticSerializationUnexpectedValue: PydanticSerializationUnexpectedValue: Expected `ImageContentItem` but got `list` with value `['Michael Jordan was born...ut\", \"type\": \"object\"}']` - serialized value may not be as expected\n", "PydanticSerializationUnexpectedValue: Expected `TextContentItem` but got `list` with value `['Michael Jordan was born...ut\", \"type\": \"object\"}']` - serialized value may not be as expected\n", @@ -1760,7 +1590,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 9, "id": "sUJKJxvAFCaI", "metadata": { "colab": { @@ -1878,7 +1708,7 @@ } ], "source": [ - "available_shields = [shield.identifier for shield in client.shields.list().data]\n", + "available_shields = [shield.identifier for shield in client.shields.list()]\n", "print(\"Available Shields:\", available_shields)\n", "\n", "unsafe_examples = [\n", @@ -1939,7 +1769,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "id": "MpMXiMCv97X5", "metadata": { "colab": { @@ -1954,9 +1784,9 @@ "data": { "text/html": [ "
ToolGroup(\n",
-              "identifier='builtin::websearch',\n",
-              "provider_id='tavily-search',\n",
-              "provider_resource_id='builtin::websearch',\n",
+              "identifier='builtin::code_interpreter',\n",
+              "provider_id='code-interpreter',\n",
+              "provider_resource_id='builtin::code_interpreter',\n",
               "type='tool_group',\n",
               "args=None,\n",
               "mcp_endpoint=None\n",
@@ -1965,9 +1795,9 @@
             ],
             "text/plain": [
               "\u001b[1;35mToolGroup\u001b[0m\u001b[1m(\u001b[0m\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::websearch'\u001b[0m,\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'tavily-search'\u001b[0m,\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::websearch'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::code_interpreter'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'code-interpreter'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::code_interpreter'\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool_group'\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33margs\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mmcp_endpoint\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
@@ -2008,9 +1838,9 @@
           "data": {
             "text/html": [
               "
ToolGroup(\n",
-              "identifier='builtin::code_interpreter',\n",
-              "provider_id='code-interpreter',\n",
-              "provider_resource_id='builtin::code_interpreter',\n",
+              "identifier='builtin::websearch',\n",
+              "provider_id='tavily-search',\n",
+              "provider_resource_id='builtin::websearch',\n",
               "type='tool_group',\n",
               "args=None,\n",
               "mcp_endpoint=None\n",
@@ -2019,9 +1849,9 @@
             ],
             "text/plain": [
               "\u001b[1;35mToolGroup\u001b[0m\u001b[1m(\u001b[0m\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::code_interpreter'\u001b[0m,\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'code-interpreter'\u001b[0m,\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::code_interpreter'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::websearch'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'tavily-search'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::websearch'\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool_group'\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33margs\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mmcp_endpoint\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
@@ -2056,7 +1886,7 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 16,
+      "execution_count": 12,
       "id": "WS8Gu5b0APHs",
       "metadata": {
         "colab": {
@@ -2070,13 +1900,14 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "User> Hello\n",
-            "inference> Hello. How can I assist you today?\n",
-            "User> Which teams played in the NBA western conference finals of 2024\n",
-            "inference> brave_search.call(query=\"NBA Western Conference Finals 2024 teams\")\n",
-            "tool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\n",
-            "tool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.9310187, \"raw_content\": null}, {\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.8914433, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.8884594, \"raw_content\": null}, {\"title\": \"NBA Conference Finals Schedule: Full List of Games & Results\", \"url\": \"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\", \"content\": \"The 2024 NBA conference finals matchups are set. Here's the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\", \"score\": 0.850382, \"raw_content\": null}, {\"title\": \"2024 NBA Western Conference playoff bracket - Basketnews.com\", \"url\": \"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\", \"content\": \"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\", \"score\": 0.8473754, \"raw_content\": null}]}\n",
-            "inference> The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\n"
+            "\u001b[32mUser> Hello\u001b[0m\n",
+            "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[33mHello\u001b[0m\u001b[33m.\u001b[0m\u001b[33m How\u001b[0m\u001b[33m can\u001b[0m\u001b[33m I\u001b[0m\u001b[33m assist\u001b[0m\u001b[33m you\u001b[0m\u001b[33m today\u001b[0m\u001b[33m?\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[30m\u001b[0m\u001b[32mUser> Which teams played in the NBA western conference finals of 2024\u001b[0m\n",
+            "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mN\u001b[0m\u001b[36mBA\u001b[0m\u001b[36m Western\u001b[0m\u001b[36m Conference\u001b[0m\u001b[36m Finals\u001b[0m\u001b[36m \u001b[0m\u001b[36m202\u001b[0m\u001b[36m4\u001b[0m\u001b[36m teams\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\u001b[0m\n",
+            "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.9310187, \"raw_content\": null}, {\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.8914433, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.8884594, \"raw_content\": null}, {\"title\": \"NBA Conference Finals Schedule: Full List of Games & Results\", \"url\": \"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\", \"content\": \"The 2024 NBA conference finals matchups are set. Here's the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\", \"score\": 0.850382, \"raw_content\": null}, {\"title\": \"2024 NBA Western Conference playoff bracket - Basketnews.com\", \"url\": \"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\", \"content\": \"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\", \"score\": 0.8473754, \"raw_content\": null}]}\u001b[0m\n",
+            "\u001b[33minference> \u001b[0m\u001b[33mThe\u001b[0m\u001b[33m teams\u001b[0m\u001b[33m that\u001b[0m\u001b[33m played\u001b[0m\u001b[33m in\u001b[0m\u001b[33m the\u001b[0m\u001b[33m NBA\u001b[0m\u001b[33m Western\u001b[0m\u001b[33m Conference\u001b[0m\u001b[33m Finals\u001b[0m\u001b[33m of\u001b[0m\u001b[33m \u001b[0m\u001b[33m202\u001b[0m\u001b[33m4\u001b[0m\u001b[33m were\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Dallas\u001b[0m\u001b[33m Mavericks\u001b[0m\u001b[33m and\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Minnesota\u001b[0m\u001b[33m Timber\u001b[0m\u001b[33mw\u001b[0m\u001b[33molves\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[30m\u001b[0m"
           ]
         }
       ],
@@ -2084,6 +1915,7 @@
         "from llama_stack_client.lib.agents.agent import Agent\n",
         "from llama_stack_client.lib.agents.event_logger import EventLogger\n",
         "from llama_stack_client.types.agent_create_params import AgentConfig\n",
+        "from termcolor import cprint\n",
         "\n",
         "agent_config = AgentConfig(\n",
         "    model=model_id,\n",
@@ -2131,7 +1963,7 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 17,
+      "execution_count": 13,
       "id": "GvLWltzZCNkg",
       "metadata": {
         "colab": {
@@ -2202,7 +2034,7 @@
         {
           "data": {
             "application/vnd.jupyter.widget-view+json": {
-              "model_id": "edc4d84302f746d39a43e8107af6b67b",
+              "model_id": "3e764c00c08942caa2ccb6b92ee60a4e",
               "version_major": 2,
               "version_minor": 0
             },
@@ -2216,7 +2048,7 @@
         {
           "data": {
             "application/vnd.jupyter.widget-view+json": {
-              "model_id": "2eff72cbd9bb4f1ca77213602caa9417",
+              "model_id": "af6680f2e60e476d8487aea98a23b84e",
               "version_major": 2,
               "version_minor": 0
             },
@@ -2230,7 +2062,7 @@
         {
           "data": {
             "application/vnd.jupyter.widget-view+json": {
-              "model_id": "7cc356ed20e94401b72a0e138ad0f5df",
+              "model_id": "c26a9d456e904b2b900bf5e0a5964a0d",
               "version_major": 2,
               "version_minor": 0
             },
@@ -2244,7 +2076,7 @@
         {
           "data": {
             "application/vnd.jupyter.widget-view+json": {
-              "model_id": "472b1acc4c5a4c48b2ec62be42d1830c",
+              "model_id": "5a3e0b5ae83143329de6507f9bcf83e0",
               "version_major": 2,
               "version_minor": 0
             },
@@ -2259,13 +2091,14 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "User> What are the top 5 topics that were explained? Only list succinct bullet points.\n"
+            "\u001b[32mUser> What are the top 5 topics that were explained? Only list succinct bullet points.\u001b[0m\n",
+            "\u001b[30m\u001b[0m"
           ]
         },
         {
           "data": {
             "application/vnd.jupyter.widget-view+json": {
-              "model_id": "15ae23892b634a9f821a8fcee14e500b",
+              "model_id": "3c9bc5588765436da4f1fee2d893cafd",
               "version_major": 2,
               "version_minor": 0
             },
@@ -2280,15 +2113,16 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "tool_execution> Tool:query_memory Args:{}\n",
-            "tool_execution> fetched 10848 bytes from memory\n",
-            "inference> Here are the top 5 topics explained:\n",
+            "\u001b[32mtool_execution> Tool:query_memory Args:{}\u001b[0m\n",
+            "\u001b[36mtool_execution> fetched 11069 bytes from memory\u001b[0m\n",
+            "\u001b[33minference> \u001b[0m\u001b[33mHere\u001b[0m\u001b[33m are\u001b[0m\u001b[33m the\u001b[0m\u001b[33m top\u001b[0m\u001b[33m \u001b[0m\u001b[33m5\u001b[0m\u001b[33m topics\u001b[0m\u001b[33m that\u001b[0m\u001b[33m were\u001b[0m\u001b[33m explained\u001b[0m\u001b[33m:\n",
             "\n",
-            "• Fine-tuning on a custom chat dataset\n",
-            "• Tokenizing prompt templates & special tokens\n",
-            "• Template changes from Llama2 to Llama3\n",
-            "• When to use a prompt template\n",
-            "• Fine-tuning Llama3 with chat data\n"
+            "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Fine\u001b[0m\u001b[33m-t\u001b[0m\u001b[33muning\u001b[0m\u001b[33m a\u001b[0m\u001b[33m model\u001b[0m\u001b[33m to\u001b[0m\u001b[33m expect\u001b[0m\u001b[33m a\u001b[0m\u001b[33m certain\u001b[0m\u001b[33m prompt\u001b[0m\u001b[33m structure\u001b[0m\u001b[33m on\u001b[0m\u001b[33m inference\u001b[0m\u001b[33m for\u001b[0m\u001b[33m a\u001b[0m\u001b[33m specific\u001b[0m\u001b[33m task\u001b[0m\u001b[33m\n",
+            "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Fine\u001b[0m\u001b[33m-t\u001b[0m\u001b[33muning\u001b[0m\u001b[33m on\u001b[0m\u001b[33m a\u001b[0m\u001b[33m custom\u001b[0m\u001b[33m chat\u001b[0m\u001b[33m dataset\u001b[0m\u001b[33m\n",
+            "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Token\u001b[0m\u001b[33mizing\u001b[0m\u001b[33m prompt\u001b[0m\u001b[33m templates\u001b[0m\u001b[33m and\u001b[0m\u001b[33m special\u001b[0m\u001b[33m tokens\u001b[0m\u001b[33m\n",
+            "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Using\u001b[0m\u001b[33m the\u001b[0m\u001b[33m L\u001b[0m\u001b[33mlama\u001b[0m\u001b[33m2\u001b[0m\u001b[33mChat\u001b[0m\u001b[33mTemplate\u001b[0m\u001b[33m class\u001b[0m\u001b[33m to\u001b[0m\u001b[33m format\u001b[0m\u001b[33m messages\u001b[0m\u001b[33m\n",
+            "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Token\u001b[0m\u001b[33mizing\u001b[0m\u001b[33m examples\u001b[0m\u001b[33m with\u001b[0m\u001b[33m the\u001b[0m\u001b[33m L\u001b[0m\u001b[33mlama\u001b[0m\u001b[33m2\u001b[0m\u001b[33m tokenizer\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[30m\u001b[0m"
           ]
         }
       ],
@@ -2583,7 +2417,7 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 20,
+      "execution_count": 16,
       "id": "4iCO59kP20Zs",
       "metadata": {
         "colab": {
@@ -2597,18 +2431,26 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "inference> brave_search.call(query=\"NBA Western Conference Finals 2024 teams\")\n",
-            "tool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\n",
-            "tool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.9310187, \"raw_content\": null}, {\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.8914433, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.8884594, \"raw_content\": null}, {\"title\": \"NBA Conference Finals Schedule: Full List of Games & Results\", \"url\": \"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\", \"content\": \"The 2024 NBA conference finals matchups are set. Here's the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\", \"score\": 0.85008353, \"raw_content\": null}, {\"title\": \"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\", \"url\": \"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\", \"content\": \"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\", \"score\": 0.81979275, \"raw_content\": null}]}\n",
-            "inference> The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\n",
-            "inference> brave_search.call(query=\"Bill Cosby South Park episode\")\n",
-            "tool_execution> Tool:brave_search Args:{'query': 'Bill Cosby South Park episode'}\n",
-            "tool_execution> Tool:brave_search Response:{\"query\": \"Bill Cosby South Park episode\", \"top_k\": [{\"title\": \"Bill Cosby | South Park Archives | Fandom\", \"url\": \"https://southpark.fandom.com/wiki/Bill_Cosby\", \"content\": \"For other uses, see Bill (Disambiguation). William Henry \\\"Bill\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\"Here Comes the Neighborhood\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\"#HappyHolograms\\\" where he is shown trying to molest pop star Taylor\", \"score\": 0.82288796, \"raw_content\": null}, {\"title\": \"Trapper Keeper (South Park) - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\", \"content\": \"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. [1] The main plot of the episode involving the Trapper Keeper was written before the election, [1]\", \"score\": 0.75659186, \"raw_content\": null}, {\"title\": \"Bill Cosby is Here to See You - South Park Studios US\", \"url\": \"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\", \"content\": \"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\"Cartman Bra\\\" South Park S18 E9.\", \"score\": 0.7156829, \"raw_content\": null}, {\"title\": \"Bill Cosby and Taylor Swift Duet - South Park Studios\", \"url\": \"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\", \"content\": \"The holiday special continues with Bill Cosby and Taylor Swift's rendition of \\\"It's Snowing Out There\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\u2022 12/10/2014. The\", \"score\": 0.64639384, \"raw_content\": null}, {\"title\": \"Bill Cosby (android) | South Park Character ... - South Park Studios US\", \"url\": \"https://southpark.cc.com/wiki/Bill_Cosby_(android)\", \"content\": \"About. Sent back in time to destroy Eric Cartman's Dawson's Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\"Bill Cosby\\\" is really VSM471, an android or cyborg of some kind engineered by 'hoomans' in the distant future. He fails in his initial missions to infiltrate South Park Elementary's 4th Grade class, destroy the Trapper Keeper or\", \"score\": 0.56460327, \"raw_content\": null}]}\n",
-            "inference> Bill Cosby (BSM-471) first appears in the Season 4 episode \"Trapper Keeper\" of South Park.\n",
-            "inference> brave_search.call(query=\"Andrew Tate kickboxing name\")\n",
-            "tool_execution> Tool:brave_search Args:{'query': 'Andrew Tate kickboxing name'}\n",
-            "tool_execution> Tool:brave_search Response:{\"query\": \"Andrew Tate kickboxing name\", \"top_k\": [{\"title\": \"50 Facts About Andrew Tate - Facts.net\", \"url\": \"https://facts.net/andrew-tate-facts/\", \"content\": \"Full Name: Andrew Tate's full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\", \"score\": 0.8967681, \"raw_content\": null}, {\"title\": \"The Life Of Andrew Tate (By Andrew Tate Himself)\", \"url\": \"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\", \"content\": \"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate's Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\", \"score\": 0.8795718, \"raw_content\": null}, {\"title\": \"About Andrew Tate | The Real World\", \"url\": \"https://www.taterealworldofficial.com/about-andrew-tate\", \"content\": \"Emory Andrew Tate III (born December 14, 1986) is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\", \"score\": 0.8386933, \"raw_content\": null}, {\"title\": \"Andrew Tate - Fight Record - Muay Thai Records\", \"url\": \"https://muaythairecords.com/fighters/andrew-tate\", \"content\": \"Andrew \\\"King Cobra\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\u2032 4\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\u00eet. 14th Mar 2015 -Boxe in D\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\u00eet by decision. ... Name: Andrew Tate\", \"score\": 0.8194462, \"raw_content\": null}, {\"title\": \"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\", \"url\": \"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\", \"content\": \"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\", \"score\": 0.7992077, \"raw_content\": null}]}\n",
-            "inference> Andrew Tate's kickboxing name is \"King Cobra\" or \"Cobra Tate\".\n"
+            "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m"
+          ]
+        },
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mN\u001b[0m\u001b[36mBA\u001b[0m\u001b[36m Western\u001b[0m\u001b[36m Conference\u001b[0m\u001b[36m Finals\u001b[0m\u001b[36m \u001b[0m\u001b[36m202\u001b[0m\u001b[36m4\u001b[0m\u001b[36m teams\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\u001b[0m\n",
+            "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.9310187, \"raw_content\": null}, {\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.8914433, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.8884594, \"raw_content\": null}, {\"title\": \"NBA Conference Finals Schedule: Full List of Games & Results\", \"url\": \"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\", \"content\": \"The 2024 NBA conference finals matchups are set. Here's the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\", \"score\": 0.85008353, \"raw_content\": null}, {\"title\": \"2024 NBA Western Conference playoff bracket - Basketnews.com\", \"url\": \"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\", \"content\": \"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\", \"score\": 0.8479807, \"raw_content\": null}]}\u001b[0m\n",
+            "\u001b[33minference> \u001b[0m\u001b[33mThe\u001b[0m\u001b[33m teams\u001b[0m\u001b[33m that\u001b[0m\u001b[33m played\u001b[0m\u001b[33m in\u001b[0m\u001b[33m the\u001b[0m\u001b[33m NBA\u001b[0m\u001b[33m Western\u001b[0m\u001b[33m Conference\u001b[0m\u001b[33m Finals\u001b[0m\u001b[33m of\u001b[0m\u001b[33m \u001b[0m\u001b[33m202\u001b[0m\u001b[33m4\u001b[0m\u001b[33m were\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Dallas\u001b[0m\u001b[33m Mavericks\u001b[0m\u001b[33m and\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Minnesota\u001b[0m\u001b[33m Timber\u001b[0m\u001b[33mw\u001b[0m\u001b[33molves\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[30m\u001b[0m\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mBill\u001b[0m\u001b[36m Cosby\u001b[0m\u001b[36m South\u001b[0m\u001b[36m Park\u001b[0m\u001b[36m episode\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'Bill Cosby South Park episode'}\u001b[0m\n",
+            "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Bill Cosby South Park episode\", \"top_k\": [{\"title\": \"Bill Cosby and Taylor Swift Duet - South Park Studios\", \"url\": \"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\", \"content\": \"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift's rendition of \\\"It's Snowing Out There\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman's plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\", \"score\": 0.685971, \"raw_content\": null}, {\"title\": \"Bill Cosby is Here to See You - South Park Studios US\", \"url\": \"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\", \"content\": \"01:56 It's Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde's performance and calls the Record Producer to try and fix it. 01:24 Lorde's Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac's hologram. 01:37 I've Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\", \"score\": 0.6643884, \"raw_content\": null}, {\"title\": \"Bill Cosby (android) | South Park Character ... - South Park Studios US\", \"url\": \"https://southpark.cc.com/wiki/Bill_Cosby_(android)\", \"content\": \"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman's Dawson's Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\"Bill Cosby\\\" is really VSM471, an android or cyborg of some kind engineered by 'hoomans' in the distant future. He fails in his initial missions to infiltrate South Park Elementary's 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski's aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\", \"score\": 0.5052006, \"raw_content\": null}, {\"title\": \"'South Park' takes on Cosby, police, 2014 | CNN\", \"url\": \"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\", \"content\": \"\\u2018South Park\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\u00a0\\u2014\\u00a0 \\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\u201d wrote Brent Veale. \\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\", \"score\": 0.45391592, \"raw_content\": null}, {\"title\": \"Trapper Keeper (South Park) - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\", \"content\": \"\\\"Trapper Keeper\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman's new Trapper Keeper, while Mr. Garrison's kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election's outcome.[2] \\\"Trapper Keeper\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\"Trapper Keeper\\\" Full episode at South Park Studios\", \"score\": 0.3839421, \"raw_content\": null}]}\u001b[0m\n",
+            "\u001b[33minference> \u001b[0m\u001b[33mBill\u001b[0m\u001b[33m Cosby\u001b[0m\u001b[33m (\u001b[0m\u001b[33mBS\u001b[0m\u001b[33mM\u001b[0m\u001b[33m-\u001b[0m\u001b[33m471\u001b[0m\u001b[33m)\u001b[0m\u001b[33m first\u001b[0m\u001b[33m appears\u001b[0m\u001b[33m in\u001b[0m\u001b[33m Season\u001b[0m\u001b[33m \u001b[0m\u001b[33m4\u001b[0m\u001b[33m,\u001b[0m\u001b[33m Episode\u001b[0m\u001b[33m \u001b[0m\u001b[33m12\u001b[0m\u001b[33m of\u001b[0m\u001b[33m South\u001b[0m\u001b[33m Park\u001b[0m\u001b[33m,\u001b[0m\u001b[33m titled\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mTr\u001b[0m\u001b[33mapper\u001b[0m\u001b[33m Keeper\u001b[0m\u001b[33m\".\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[30m\u001b[0m\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mAndrew\u001b[0m\u001b[36m Tate\u001b[0m\u001b[36m kick\u001b[0m\u001b[36mboxing\u001b[0m\u001b[36m name\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'Andrew Tate kickboxing name'}\u001b[0m\n",
+            "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Andrew Tate kickboxing name\", \"top_k\": [{\"title\": \"50 Facts About Andrew Tate - Facts.net\", \"url\": \"https://facts.net/andrew-tate-facts/\", \"content\": \"Full Name: Andrew Tate's full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\", \"score\": 0.8967681, \"raw_content\": null}, {\"title\": \"The Life Of Andrew Tate (By Andrew Tate Himself)\", \"url\": \"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\", \"content\": \"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate's Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\", \"score\": 0.8795718, \"raw_content\": null}, {\"title\": \"Andrew Tate kickboxing record: How many championships ... - FirstSportz\", \"url\": \"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\", \"content\": \"Andrew Tate's Kickboxing career. During his kickboxing career, he used the nickname \\\"King Cobra,\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\", \"score\": 0.8752871, \"raw_content\": null}, {\"title\": \"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\", \"url\": \"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\", \"content\": \"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\", \"score\": 0.7992077, \"raw_content\": null}, {\"title\": \"About Andrew Tate: A Journey from Champion to Controversy\", \"url\": \"https://reachmorpheus.com/andrew-tate/\", \"content\": \"Andrew Tate's kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\", \"score\": 0.6490677, \"raw_content\": null}]}\u001b[0m\n",
+            "\u001b[33minference> \u001b[0m\u001b[33mAndrew\u001b[0m\u001b[33m Tate\u001b[0m\u001b[33m's\u001b[0m\u001b[33m kick\u001b[0m\u001b[33mboxing\u001b[0m\u001b[33m name\u001b[0m\u001b[33m is\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mC\u001b[0m\u001b[33mobra\u001b[0m\u001b[33m Tate\u001b[0m\u001b[33m\"\u001b[0m\u001b[33m or\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mKing\u001b[0m\u001b[33m Cobra\u001b[0m\u001b[33m\".\u001b[0m\u001b[97m\u001b[0m\n",
+            "\u001b[30m\u001b[0m"
           ]
         }
       ],
@@ -2628,8 +2470,8 @@
         ")\n",
         "agent = Agent(client, agent_config)\n",
         "user_prompts = [\n",
-        "    # \"Which teams played in the NBA western conference finals of 2024\",\n",
-        "    # \"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\n",
+        "    \"Which teams played in the NBA western conference finals of 2024\",\n",
+        "    \"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\n",
         "    \"What is the British-American kickboxer Andrew Tate's kickboxing name?\",\n",
         "]\n",
         "\n",
@@ -2662,7 +2504,7 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 22,
+      "execution_count": 17,
       "id": "agkWgToGAsuA",
       "metadata": {
         "colab": {
@@ -2677,7 +2519,7 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "Getting traces for session_id=44d006af-1394-4832-9799-5f0cb0ca01d6\n"
+            "Getting traces for session_id=4c99812c-d3db-4555-a897-b592bf22b3e6\n"
           ]
         },
         {
@@ -2687,56 +2529,20 @@
               "{\n",
               "│   │   'input': [\n",
               "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill (Disambiguation). William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. [1] The main plot of the episode involving the Trapper Keeper was written before the election, [1]\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null}]}\"}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in the Season 4 episode \\\\\"Trapper Keeper\\\\\" of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}'\n",
-              "│   │   ],\n",
-              "│   │   'output': \"content:  tool_calls: [ToolCall(call_id='44705eaf-b371-4841-b0ee-5eb21a5d7f36', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\"\n",
-              "},\n",
-              "{\n",
-              "│   │   'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n",
-              "│   │   'output': '{\"role\":\"ipython\",\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate | The Real World\\\\\", \\\\\"url\\\\\": \\\\\"https://www.taterealworldofficial.com/about-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Emory Andrew Tate III (born December 14, 1986) is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\\\\\", \\\\\"score\\\\\": 0.8386933, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate - Fight Record - Muay Thai Records\\\\\", \\\\\"url\\\\\": \\\\\"https://muaythairecords.com/fighters/andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\\\\\\\u2032 4\\\\\\\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\\\\\\\u00eet. 14th Mar 2015 -Boxe in D\\\\\\\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\\\\\\\u00eet by decision. ... Name: Andrew Tate\\\\\", \\\\\"score\\\\\": 0.8194462, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}]}\"}'\n",
-              "},\n",
-              "{\n",
-              "│   │   'input': [\n",
-              "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill (Disambiguation). William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. [1] The main plot of the episode involving the Trapper Keeper was written before the election, [1]\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null}]}\"}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in the Season 4 episode \\\\\"Trapper Keeper\\\\\" of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate | The Real World\\\\\", \\\\\"url\\\\\": \\\\\"https://www.taterealworldofficial.com/about-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Emory Andrew Tate III (born December 14, 1986) is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\\\\\", \\\\\"score\\\\\": 0.8386933, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate - Fight Record - Muay Thai Records\\\\\", \\\\\"url\\\\\": \\\\\"https://muaythairecords.com/fighters/andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\\\\\\\u2032 4\\\\\\\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\\\\\\\u00eet. 14th Mar 2015 -Boxe in D\\\\\\\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\\\\\\\u00eet by decision. ... Name: Andrew Tate\\\\\", \\\\\"score\\\\\": 0.8194462, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}]}\"}'\n",
-              "│   │   ],\n",
-              "│   │   'output': 'content: Andrew Tate\\'s kickboxing name is \"King Cobra\" or \"Cobra Tate\". tool_calls: []'\n",
-              "},\n",
-              "{\n",
-              "│   │   'input': [\n",
-              "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
               "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}'\n",
-              "│   │   ],\n",
-              "│   │   'output': \"content:  tool_calls: [ToolCall(call_id='b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\"\n",
+              "│   │   ],\n",
+              "│   │   'output': \"content:  tool_calls: [ToolCall(call_id='838a3846-0bc4-488e-9e42-65a48e29b80a', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\"\n",
               "},\n",
               "{\n",
-              "│   │   'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
-              "│   │   'output': '{\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}'\n",
+              "│   │   'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
+              "│   │   'output': '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}'\n",
               "},\n",
               "{\n",
               "│   │   'input': [\n",
               "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
               "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}'\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}'\n",
               "│   │   ],\n",
               "│   │   'output': 'content: The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves. tool_calls: []'\n",
               "},\n",
@@ -2744,29 +2550,65 @@
               "│   │   'input': [\n",
               "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
               "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}',\n",
               "│   │   │   '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
               "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}'\n",
               "│   │   ],\n",
-              "│   │   'output': \"content:  tool_calls: [ToolCall(call_id='1e487e8e-a15f-4137-854a-1d4979a70b8c', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Bill Cosby South Park episode'})]\"\n",
+              "│   │   'output': \"content:  tool_calls: [ToolCall(call_id='ebd7e906-3ec9-45de-a58e-6662d75eceb7', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Bill Cosby South Park episode'})]\"\n",
+              "},\n",
+              "{\n",
+              "│   │   'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
+              "│   │   'output': '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'\n",
+              "},\n",
+              "{\n",
+              "│   │   'input': [\n",
+              "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
+              "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
+              "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'\n",
+              "│   │   ],\n",
+              "│   │   'output': 'content: Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \"Trapper Keeper\". tool_calls: []'\n",
+              "},\n",
+              "{\n",
+              "│   │   'input': [\n",
+              "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
+              "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
+              "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
+              "│   │   │   '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}'\n",
+              "│   │   ],\n",
+              "│   │   'output': \"content:  tool_calls: [ToolCall(call_id='e26ecfb2-434c-479f-95dc-7b3b4929665a', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\"\n",
               "},\n",
               "{\n",
-              "│   │   'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
-              "│   │   'output': '{\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill (Disambiguation). William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. [1] The main plot of the episode involving the Trapper Keeper was written before the election, [1]\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null}]}\"}'\n",
+              "│   │   'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n",
+              "│   │   'output': '{\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}]}\"}'\n",
               "},\n",
               "{\n",
               "│   │   'input': [\n",
               "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
               "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}',\n",
               "│   │   │   '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
               "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill (Disambiguation). William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. [1] The main plot of the episode involving the Trapper Keeper was written before the election, [1]\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null}]}\"}'\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
+              "│   │   │   '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}',\n",
+              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n",
+              "│   │   │   '{\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}]}\"}'\n",
               "│   │   ],\n",
-              "│   │   'output': 'content: Bill Cosby (BSM-471) first appears in the Season 4 episode \"Trapper Keeper\" of South Park. tool_calls: []'\n",
+              "│   │   'output': 'content: Andrew Tate\\'s kickboxing name is \"Cobra Tate\" or \"King Cobra\". tool_calls: []'\n",
               "}\n",
               "]\n",
               "
\n" @@ -2776,56 +2618,20 @@ "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \\\\\"Trapper Keeper\\\\\" of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='44705eaf-b371-4841-b0ee-5eb21a5d7f36', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate | The Real World\\\\\", \\\\\"url\\\\\": \\\\\"https://www.taterealworldofficial.com/about-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Emory Andrew Tate III \u001b[0m\u001b[32m(\u001b[0m\u001b[32mborn December 14, 1986\u001b[0m\u001b[32m)\u001b[0m\u001b[32m is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\\\\\", \\\\\"score\\\\\": 0.8386933, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate - Fight Record - Muay Thai Records\\\\\", \\\\\"url\\\\\": \\\\\"https://muaythairecords.com/fighters/andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\\\\\\\u2032 4\\\\\\\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\\\\\\\u00eet. 14th Mar 2015 -Boxe in D\\\\\\\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\\\\\\\u00eet by decision. ... Name: Andrew Tate\\\\\", \\\\\"score\\\\\": 0.8194462, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \\\\\"Trapper Keeper\\\\\" of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"44705eaf-b371-4841-b0ee-5eb21a5d7f36\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate | The Real World\\\\\", \\\\\"url\\\\\": \\\\\"https://www.taterealworldofficial.com/about-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Emory Andrew Tate III \u001b[0m\u001b[32m(\u001b[0m\u001b[32mborn December 14, 1986\u001b[0m\u001b[32m)\u001b[0m\u001b[32m is an American-British kickboxer from Chicago, Illinois, who competes in the cruiserweight and heavyweight divisions. ... Tate challenged Paul Randall for the vacant ISKA English Kickboxing Light-cruiserweight title. Tate won his first ISKA Kickboxing title stopping Randall in the fifth round of\\\\\", \\\\\"score\\\\\": 0.8386933, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate - Fight Record - Muay Thai Records\\\\\", \\\\\"url\\\\\": \\\\\"https://muaythairecords.com/fighters/andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate is a 38-year-old Muay Thai fighter. With a record of 23-8-0, including 32 knockouts, standing at 6\\\\\\\\u2032 4\\\\\\\\u2033 and weighing 198 lbs. Originally from Luton, United Kingdom. ... WIN Dec -Kickboxing Jean Luc Beno\\\\\\\\u00eet. 14th Mar 2015 -Boxe in D\\\\\\\\u00e9fi 16. Andrew Tate defeated Jean Luc Beno\\\\\\\\u00eet by decision. ... Name: Andrew Tate\\\\\", \\\\\"score\\\\\": 0.8194462, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: Andrew Tate\\'s kickboxing name is \"King Cobra\" or \"Cobra Tate\". tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='838a3846-0bc4-488e-9e42-65a48e29b80a', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", @@ -2833,29 +2639,65 @@ "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='1e487e8e-a15f-4137-854a-1d4979a70b8c', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='ebd7e906-3ec9-45de-a58e-6662d75eceb7', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in Season 4, Episode 12 of South Park, titled \"Trapper Keeper\". tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='e26ecfb2-434c-479f-95dc-7b3b4929665a', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"1e487e8e-a15f-4137-854a-1d4979a70b8c\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"For other uses, see Bill \u001b[0m\u001b[32m(\u001b[0m\u001b[32mDisambiguation\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. William Henry \\\\\\\\\\\\\"Bill\\\\\\\\\\\\\" Cosby Jr. African-American comedian, actor, and serial rapist. He first appears in the Season Five episode, \\\\\\\\\\\\\"Here Comes the Neighborhood\\\\\\\\\\\\\", as one of the wealthy African-Americans who move to South Park. He returned as a hologram in the Season Eighteen episode, \\\\\\\\\\\\\"#HappyHolograms\\\\\\\\\\\\\" where he is shown trying to molest pop star Taylor\\\\\", \\\\\"score\\\\\": 0.82288796, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby warns that if the Trapper Keeper assimilates with the supercomputer at Cheyenne Mountain, it will become unstoppable. ... It is one of the many South Park episodes that parodies a current event. \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election, \u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\\\\\", \\\\\"score\\\\\": 0.75659186, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. ... South Park. Bill Cosby is Here to See You. Season 18 E 10 \\\\\\\\u2022 12/10/2014. Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. More. Watch Random Episode. Watching. 01:11. Please Welcome \\\\\\\\\\\\\"Cartman Bra\\\\\\\\\\\\\" South Park S18 E9.\\\\\", \\\\\"score\\\\\": 0.7156829, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". ... Full Episodes. Collections. Random Episode. Full Episodes. Events. Wiki. News. Avatar. Shop. Forum. Games. South Park. Menu. Episodes & Videos. About. South Park. Bill Cosby and Taylor Swift Duet. Season 18 E 10 \\\\\\\\u2022 12/10/2014. The\\\\\", \\\\\"score\\\\\": 0.64639384, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"About. Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or\\\\\", \\\\\"score\\\\\": 0.56460327, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'content: Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the Season 4 episode \"Trapper Keeper\" of South Park. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'content: Andrew Tate\\'s kickboxing name is \"Cobra Tate\" or \"King Cobra\". tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", "\u001b[1m]\u001b[0m\n" ] @@ -2899,7 +2741,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 19, "id": "sy4Xaff_Avuu", "metadata": { "colab": { @@ -2910,23 +2752,38 @@ "outputId": "1b14b5ed-4c77-47c4-edfb-1c13a88e5ef4" }, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}'], 'output': \"content: tool_calls: [ToolCall(call_id='838a3846-0bc4-488e-9e42-65a48e29b80a', tool_name=, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\"}\n", + "{'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', 'output': '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}'], 'output': 'content: The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves. tool_calls: []'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}'], 'output': \"content: tool_calls: [ToolCall(call_id='ebd7e906-3ec9-45de-a58e-6662d75eceb7', tool_name=, arguments={'query': 'Bill Cosby South Park episode'})]\"}\n", + "{'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', 'output': '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'], 'output': 'content: Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \"Trapper Keeper\". tool_calls: []'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}'], 'output': \"content: tool_calls: [ToolCall(call_id='e26ecfb2-434c-479f-95dc-7b3b4929665a', tool_name=, arguments={'query': 'Andrew Tate kickboxing name'})]\"}\n", + "{'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}', 'output': '{\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}]}\"}'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}', '{\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}]}\"}'], 'output': 'content: Andrew Tate\\'s kickboxing name is \"Cobra Tate\" or \"King Cobra\". tool_calls: []'}\n" + ] + }, { "data": { "text/html": [ "
[\n",
               "{\n",
-              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}',\n",
-              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='44705eaf-b371-4841-b0ee-5eb21a5d7f36', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\",\n",
-              "│   │   'expected_answer': 'brave_search'\n",
-              "},\n",
-              "{\n",
-              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\",\n",
+              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
+              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='838a3846-0bc4-488e-9e42-65a48e29b80a', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\",\n",
               "│   │   'expected_answer': 'brave_search'\n",
               "},\n",
               "{\n",
               "│   │   'input_query': '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
-              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='1e487e8e-a15f-4137-854a-1d4979a70b8c', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Bill Cosby South Park episode'})]\",\n",
+              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='ebd7e906-3ec9-45de-a58e-6662d75eceb7', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Bill Cosby South Park episode'})]\",\n",
+              "│   │   'expected_answer': 'brave_search'\n",
+              "},\n",
+              "{\n",
+              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}',\n",
+              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='e26ecfb2-434c-479f-95dc-7b3b4929665a', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\",\n",
               "│   │   'expected_answer': 'brave_search'\n",
               "}\n",
               "]\n",
@@ -2935,18 +2792,18 @@
             "text/plain": [
               "\u001b[1m[\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1m{\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='44705eaf-b371-4841-b0ee-5eb21a5d7f36', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[1;39m{\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='b7d9e0dd-4d6d-47db-9d81-3d7834f6e53d', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='838a3846-0bc4-488e-9e42-65a48e29b80a', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1;39m{\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='1e487e8e-a15f-4137-854a-1d4979a70b8c', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m,\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='ebd7e906-3ec9-45de-a58e-6662d75eceb7', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[1;39m{\u001b[0m\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='e26ecfb2-434c-479f-95dc-7b3b4929665a', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m,\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'expected_answer'\u001b[0m: \u001b[32m'brave_search'\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
               "\u001b[1m]\u001b[0m\n"
@@ -3029,7 +2886,7 @@
     },
     {
       "cell_type": "code",
-      "execution_count": 24,
+      "execution_count": 20,
       "id": "xG4Y84VQBb0g",
       "metadata": {
         "colab": {
@@ -3050,7 +2907,7 @@
               "│   │   │   score_rows=[\n",
               "│   │   │   │   {\n",
               "│   │   │   │   │   'score': 'B',\n",
-              "│   │   │   │   │   'judge_feedback': \"Answer: B, Explanation: The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE as it provides more detailed information about the topics related to LoRA (although it does list more than one topic as does not exactly follow the desired format of only giving one 'topic', while the EXPECTED_RESPONSE simply lists 'LoRA').\"\n",
+              "│   │   │   │   │   'judge_feedback': 'Answer: B, Explanation: The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE and is fully consistent with it. The EXPECTED_RESPONSE only mentions \"LoRA\", which is present in all the points of the GENERATED_RESPONSE. The GENERATED_RESPONSE provides more details and specific topics related to LoRA, but it does not contradict the EXPECTED_RESPONSE.'\n",
               "│   │   │   │   }\n",
               "│   │   │   ]\n",
               "│   │   ),\n",
@@ -3070,7 +2927,7 @@
               "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\n",
               "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m{\u001b[0m\n",
               "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m,\n",
-              "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'judge_feedback'\u001b[0m: \u001b[32m\"Answer: B, Explanation: The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE as it provides more detailed information about the topics related to LoRA \u001b[0m\u001b[32m(\u001b[0m\u001b[32malthough it does list more than one topic as does not exactly follow the desired format of only giving one 'topic', while the EXPECTED_RESPONSE simply lists 'LoRA'\u001b[0m\u001b[32m)\u001b[0m\u001b[32m.\"\u001b[0m\n",
+              "\u001b[2;32m│   │   │   │   │   \u001b[0m\u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'Answer: B, Explanation: The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE and is fully consistent with it. The EXPECTED_RESPONSE only mentions \"LoRA\", which is present in all the points of the GENERATED_RESPONSE. The GENERATED_RESPONSE provides more details and specific topics related to LoRA, but it does not contradict the EXPECTED_RESPONSE.'\u001b[0m\n",
               "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m}\u001b[0m\n",
               "\u001b[2;32m│   │   │   \u001b[0m\u001b[1m]\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m)\u001b[0m,\n",

From f1faa9c92432d0f4e910f65e8c7ba5fa53d2917d Mon Sep 17 00:00:00 2001
From: Hardik Shah 
Date: Thu, 16 Jan 2025 14:09:59 -0800
Subject: [PATCH 037/107] pop fix

---
 docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
index e5c1a4dc1..bed1aa2a8 100644
--- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
+++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
@@ -1321,7 +1321,7 @@
         "def chat_loop():\n",
         "    conversation_history = []\n",
         "    while len(questions) > 0:\n",
-        "        user_input = questions.pop()\n",
+        "        user_input = questions.pop(0)\n",
         "        if user_input.lower() in [\"exit\", \"quit\", \"bye\"]:\n",
         "            cprint(\"Ending conversation. Goodbye!\", \"yellow\")\n",
         "            break\n",

From 03ac84a829c30b5d3ccc6c783cf917b8ac690e91 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Thu, 16 Jan 2025 15:26:48 -0800
Subject: [PATCH 038/107] Update default port from 5000 -> 8321

---
 distributions/bedrock/compose.yaml                        | 2 +-
 distributions/cerebras/compose.yaml                       | 2 +-
 distributions/dell-tgi/compose.yaml                       | 2 +-
 distributions/fireworks/compose.yaml                      | 2 +-
 distributions/meta-reference-gpu/compose.yaml             | 2 +-
 distributions/meta-reference-quantized-gpu/compose.yaml   | 2 +-
 distributions/remote-nvidia/compose.yaml                  | 2 +-
 distributions/together/compose.yaml                       | 2 +-
 distributions/vllm-gpu/compose.yaml                       | 2 +-
 docs/source/building_applications/telemetry.md            | 8 ++++----
 docs/source/distributions/building_distro.md              | 4 ++--
 docs/source/distributions/ondevice_distro/ios_sdk.md      | 2 +-
 docs/source/distributions/self_hosted_distro/dell-tgi.md  | 4 ++--
 .../source/references/llama_stack_client_cli_reference.md | 4 ++--
 docs/zero_to_hero_guide/01_Local_Cloud_Inference101.ipynb | 6 +++---
 llama_stack/cli/stack/run.py                              | 4 ++--
 llama_stack/distribution/server/server.py                 | 2 +-
 llama_stack/distribution/ui/modules/api.py                | 2 +-
 18 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/distributions/bedrock/compose.yaml b/distributions/bedrock/compose.yaml
index f988e33d1..055b92c67 100644
--- a/distributions/bedrock/compose.yaml
+++ b/distributions/bedrock/compose.yaml
@@ -5,7 +5,7 @@ services:
       - ~/.llama:/root/.llama
       - ./run.yaml:/root/llamastack-run-bedrock.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml_config /root/llamastack-run-bedrock.yaml"
     deploy:
       restart_policy:
diff --git a/distributions/cerebras/compose.yaml b/distributions/cerebras/compose.yaml
index f2e9a6f42..8dc09a865 100644
--- a/distributions/cerebras/compose.yaml
+++ b/distributions/cerebras/compose.yaml
@@ -6,7 +6,7 @@ services:
       - ~/.llama:/root/.llama
       - ./run.yaml:/root/llamastack-run-cerebras.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml_config /root/llamastack-run-cerebras.yaml"
     deploy:
       restart_policy:
diff --git a/distributions/dell-tgi/compose.yaml b/distributions/dell-tgi/compose.yaml
index 0e325aff5..d26636cbd 100644
--- a/distributions/dell-tgi/compose.yaml
+++ b/distributions/dell-tgi/compose.yaml
@@ -40,7 +40,7 @@ services:
       # Link to TGI run.yaml file
       - ./run.yaml:/root/my-run.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     # Hack: wait for TGI server to start before starting docker
     entrypoint: bash -c "sleep 60; python -m llama_stack.distribution.server.server --yaml_config /root/my-run.yaml"
     restart_policy:
diff --git a/distributions/fireworks/compose.yaml b/distributions/fireworks/compose.yaml
index 71137c040..4b53fcf00 100644
--- a/distributions/fireworks/compose.yaml
+++ b/distributions/fireworks/compose.yaml
@@ -6,7 +6,7 @@ services:
       - ~/.llama:/root/.llama
       - ./run.yaml:/root/llamastack-run-fireworks.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml_config /root/llamastack-run-fireworks.yaml"
     deploy:
       restart_policy:
diff --git a/distributions/meta-reference-gpu/compose.yaml b/distributions/meta-reference-gpu/compose.yaml
index 2b88c68fc..d977e92ea 100644
--- a/distributions/meta-reference-gpu/compose.yaml
+++ b/distributions/meta-reference-gpu/compose.yaml
@@ -6,7 +6,7 @@ services:
       - ~/.llama:/root/.llama
       - ./run.yaml:/root/my-run.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     devices:
       - nvidia.com/gpu=all
     environment:
diff --git a/distributions/meta-reference-quantized-gpu/compose.yaml b/distributions/meta-reference-quantized-gpu/compose.yaml
index f9fe9f45d..98e943dce 100644
--- a/distributions/meta-reference-quantized-gpu/compose.yaml
+++ b/distributions/meta-reference-quantized-gpu/compose.yaml
@@ -6,7 +6,7 @@ services:
       - ~/.llama:/root/.llama
       - ./run.yaml:/root/my-run.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     devices:
       - nvidia.com/gpu=all
     environment:
diff --git a/distributions/remote-nvidia/compose.yaml b/distributions/remote-nvidia/compose.yaml
index 04b12d0da..ab8b4ce25 100644
--- a/distributions/remote-nvidia/compose.yaml
+++ b/distributions/remote-nvidia/compose.yaml
@@ -6,7 +6,7 @@ services:
       - ~/.llama:/root/.llama
       - ./run.yaml:/root/llamastack-run-nvidia.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     environment:
       - INFERENCE_MODEL=${INFERENCE_MODEL:-Llama3.1-8B-Instruct}
       - NVIDIA_API_KEY=${NVIDIA_API_KEY:-}
diff --git a/distributions/together/compose.yaml b/distributions/together/compose.yaml
index 8d938990e..c7251d0a7 100644
--- a/distributions/together/compose.yaml
+++ b/distributions/together/compose.yaml
@@ -6,7 +6,7 @@ services:
       - ~/.llama:/root/.llama
       - ./run.yaml:/root/llamastack-run-together.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml_config /root/llamastack-run-together.yaml"
     deploy:
       restart_policy:
diff --git a/distributions/vllm-gpu/compose.yaml b/distributions/vllm-gpu/compose.yaml
index f8779c9ce..98267cdc3 100644
--- a/distributions/vllm-gpu/compose.yaml
+++ b/distributions/vllm-gpu/compose.yaml
@@ -6,7 +6,7 @@ services:
       - ~/.llama:/root/.llama
       - ./run.yaml:/root/my-run.yaml
     ports:
-      - "5000:5000"
+      - "8321:8321"
     devices:
       - nvidia.com/gpu=all
     environment:
diff --git a/docs/source/building_applications/telemetry.md b/docs/source/building_applications/telemetry.md
index 6c8067035..70c54ac98 100644
--- a/docs/source/building_applications/telemetry.md
+++ b/docs/source/building_applications/telemetry.md
@@ -139,7 +139,7 @@ Querying Traces for a agent session
 The client SDK is not updated to support the new telemetry API. It will be updated soon. You can manually query traces using the following curl command:
 
 ``` bash
- curl -X POST 'http://localhost:5000/alpha/telemetry/query-traces' \
+ curl -X POST 'http://localhost:8321/alpha/telemetry/query-traces' \
 -H 'Content-Type: application/json' \
 -d '{
   "attribute_filters": [
@@ -167,7 +167,7 @@ The client SDK is not updated to support the new telemetry API. It will be updat
 Querying spans for a specifc root span id
 
 ``` bash
-curl -X POST 'http://localhost:5000/alpha/telemetry/get-span-tree' \
+curl -X POST 'http://localhost:8321/alpha/telemetry/get-span-tree' \
 -H 'Content-Type: application/json' \
 -d '{ "span_id" : "6cceb4b48a156913", "max_depth": 2 }'
 
@@ -207,7 +207,7 @@ curl -X POST 'http://localhost:5000/alpha/telemetry/get-span-tree' \
 ## Example: Save Spans to Dataset
 Save all spans for a specific agent session to a dataset.
 ``` bash
-curl -X POST 'http://localhost:5000/alpha/telemetry/save-spans-to-dataset' \
+curl -X POST 'http://localhost:8321/alpha/telemetry/save-spans-to-dataset' \
 -H 'Content-Type: application/json' \
 -d '{
     "attribute_filters": [
@@ -225,7 +225,7 @@ curl -X POST 'http://localhost:5000/alpha/telemetry/save-spans-to-dataset' \
 
 Save all spans for a specific agent turn to a dataset.
 ```bash
-curl -X POST 'http://localhost:5000/alpha/telemetry/save-spans-to-dataset' \
+curl -X POST 'http://localhost:8321/alpha/telemetry/save-spans-to-dataset' \
 -H 'Content-Type: application/json' \
 -d '{
     "attribute_filters": [
diff --git a/docs/source/distributions/building_distro.md b/docs/source/distributions/building_distro.md
index cc94fa9db..aaf2462f7 100644
--- a/docs/source/distributions/building_distro.md
+++ b/docs/source/distributions/building_distro.md
@@ -402,11 +402,11 @@ Serving API agents
  POST /agents/step/get
  POST /agents/turn/get
 
-Listening on ['::', '0.0.0.0']:5000
+Listening on ['::', '0.0.0.0']:8321
 INFO:     Started server process [2935911]
 INFO:     Waiting for application startup.
 INFO:     Application startup complete.
-INFO:     Uvicorn running on http://['::', '0.0.0.0']:5000 (Press CTRL+C to quit)
+INFO:     Uvicorn running on http://['::', '0.0.0.0']:8321 (Press CTRL+C to quit)
 INFO:     2401:db00:35c:2d2b:face:0:c9:0:54678 - "GET /models/list HTTP/1.1" 200 OK
 ```
 
diff --git a/docs/source/distributions/ondevice_distro/ios_sdk.md b/docs/source/distributions/ondevice_distro/ios_sdk.md
index 0c3cf09af..c9d3a89b5 100644
--- a/docs/source/distributions/ondevice_distro/ios_sdk.md
+++ b/docs/source/distributions/ondevice_distro/ios_sdk.md
@@ -27,7 +27,7 @@ If you don't want to run inference on-device, then you can connect to any hosted
 ```swift
 import LlamaStackClient
 
-let agents = RemoteAgents(url: URL(string: "http://localhost:5000")!)
+let agents = RemoteAgents(url: URL(string: "http://localhost:8321")!)
 let request = Components.Schemas.CreateAgentTurnRequest(
         agent_id: agentId,
         messages: [
diff --git a/docs/source/distributions/self_hosted_distro/dell-tgi.md b/docs/source/distributions/self_hosted_distro/dell-tgi.md
index 705bf2fa7..cf0c02983 100644
--- a/docs/source/distributions/self_hosted_distro/dell-tgi.md
+++ b/docs/source/distributions/self_hosted_distro/dell-tgi.md
@@ -41,7 +41,7 @@ The script will first start up TGI server, then start up Llama Stack distributio
 INFO:     Started server process [1]
 INFO:     Waiting for application startup.
 INFO:     Application startup complete.
-INFO:     Uvicorn running on http://[::]:5000 (Press CTRL+C to quit)
+INFO:     Uvicorn running on http://[::]:8321 (Press CTRL+C to quit)
 ```
 
 To kill the server
@@ -65,7 +65,7 @@ registry.dell.huggingface.co/enterprise-dell-inference-meta-llama-meta-llama-3.1
 #### Start Llama Stack server pointing to TGI server
 
 ```
-docker run --network host -it -p 5000:5000 -v ./run.yaml:/root/my-run.yaml --gpus=all llamastack/distribution-tgi --yaml_config /root/my-run.yaml
+docker run --network host -it -p 8321:8321 -v ./run.yaml:/root/my-run.yaml --gpus=all llamastack/distribution-tgi --yaml_config /root/my-run.yaml
 ```
 
 Make sure in you `run.yaml` file, you inference provider is pointing to the correct TGI server endpoint. E.g.
diff --git a/docs/source/references/llama_stack_client_cli_reference.md b/docs/source/references/llama_stack_client_cli_reference.md
index c3abccfd9..bc5f3e5e6 100644
--- a/docs/source/references/llama_stack_client_cli_reference.md
+++ b/docs/source/references/llama_stack_client_cli_reference.md
@@ -23,8 +23,8 @@ subcommands:
 ```bash
 $ llama-stack-client configure
 > Enter the host name of the Llama Stack distribution server: localhost
-> Enter the port number of the Llama Stack distribution server: 5000
-Done! You can now use the Llama Stack Client CLI with endpoint http://localhost:5000
+> Enter the port number of the Llama Stack distribution server: 8321
+Done! You can now use the Llama Stack Client CLI with endpoint http://localhost:8321
 ```
 
 ### `llama-stack-client providers list`
diff --git a/docs/zero_to_hero_guide/01_Local_Cloud_Inference101.ipynb b/docs/zero_to_hero_guide/01_Local_Cloud_Inference101.ipynb
index bdfd3520f..39644ee51 100644
--- a/docs/zero_to_hero_guide/01_Local_Cloud_Inference101.ipynb
+++ b/docs/zero_to_hero_guide/01_Local_Cloud_Inference101.ipynb
@@ -32,8 +32,8 @@
    "outputs": [],
    "source": [
     "HOST = \"localhost\"  # Replace with your host\n",
-    "LOCAL_PORT = 5000        # Replace with your local distro port\n",
-    "CLOUD_PORT = 5001        # Replace with your cloud distro port"
+    "LOCAL_PORT = 8321        # Replace with your local distro port\n",
+    "CLOUD_PORT = 8322        # Replace with your cloud distro port"
    ]
   },
   {
@@ -43,7 +43,7 @@
    "source": [
     "#### 2. Set Up Local and Cloud Clients\n",
     "\n",
-    "Initialize both clients, specifying the `base_url` for each instance. In this case, we have the local distribution running on `http://localhost:5000` and the cloud distribution running on `http://localhost:5001`.\n"
+    "Initialize both clients, specifying the `base_url` for each instance. In this case, we have the local distribution running on `http://localhost:8321` and the cloud distribution running on `http://localhost:5001`.\n"
    ]
   },
   {
diff --git a/llama_stack/cli/stack/run.py b/llama_stack/cli/stack/run.py
index 7942f603a..9fa82bd61 100644
--- a/llama_stack/cli/stack/run.py
+++ b/llama_stack/cli/stack/run.py
@@ -34,8 +34,8 @@ class StackRun(Subcommand):
         self.parser.add_argument(
             "--port",
             type=int,
-            help="Port to run the server on. Defaults to 5000",
-            default=int(os.getenv("LLAMA_STACK_PORT", 5000)),
+            help="Port to run the server on. Defaults to 8321",
+            default=int(os.getenv("LLAMA_STACK_PORT", 8321)),
         )
         self.parser.add_argument(
             "--image-name",
diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py
index 2d216d314..6a0047f69 100644
--- a/llama_stack/distribution/server/server.py
+++ b/llama_stack/distribution/server/server.py
@@ -293,7 +293,7 @@ def main():
     parser.add_argument(
         "--port",
         type=int,
-        default=int(os.getenv("LLAMA_STACK_PORT", 5000)),
+        default=int(os.getenv("LLAMA_STACK_PORT", 8321)),
         help="Port to listen on",
     )
     parser.add_argument(
diff --git a/llama_stack/distribution/ui/modules/api.py b/llama_stack/distribution/ui/modules/api.py
index d3852caee..70c7a0898 100644
--- a/llama_stack/distribution/ui/modules/api.py
+++ b/llama_stack/distribution/ui/modules/api.py
@@ -14,7 +14,7 @@ from llama_stack_client import LlamaStackClient
 class LlamaStackApi:
     def __init__(self):
         self.client = LlamaStackClient(
-            base_url=os.environ.get("LLAMA_STACK_ENDPOINT", "http://localhost:5000"),
+            base_url=os.environ.get("LLAMA_STACK_ENDPOINT", "http://localhost:8321"),
             provider_data={
                 "fireworks_api_key": os.environ.get("FIREWORKS_API_KEY", ""),
                 "together_api_key": os.environ.get("TOGETHER_API_KEY", ""),

From 48b12b977780105445edbc0e79c3f2f40df5cf85 Mon Sep 17 00:00:00 2001
From: Sixian Yi 
Date: Thu, 16 Jan 2025 15:33:50 -0800
Subject: [PATCH 039/107] [Test automation] generate custom test report (#739)

# What does this PR do?

Generate a test report in MD that contains two main infos:
1)  custom report on inference provider -> API / functionalities
2) [TO BE ADDED] test log for easy debugging

## Test Plan
For local testing, run test script in command line. See a test report
being generated at tests/report.html

`pytest /Users/sxyi/llama-stack/llama_stack/providers/tests/.
--config=ci_test_config.yaml`

See
[gist](https://gist.github.com/sixianyi0721/a421fd3bc450b74354a1c2c7da483fa5)
for output MD file
## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [x] Ran pre-commit to handle lint / formatting issues.
- [x] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 llama_stack/providers/tests/conftest.py |   9 ++
 llama_stack/providers/tests/report.py   | 200 ++++++++++++++++++++++++
 2 files changed, 209 insertions(+)
 create mode 100644 llama_stack/providers/tests/report.py

diff --git a/llama_stack/providers/tests/conftest.py b/llama_stack/providers/tests/conftest.py
index 9530695e1..4aa53a687 100644
--- a/llama_stack/providers/tests/conftest.py
+++ b/llama_stack/providers/tests/conftest.py
@@ -21,6 +21,7 @@ from llama_stack.distribution.datatypes import Provider
 from llama_stack.providers.datatypes import RemoteProviderConfig
 
 from .env import get_env_or_fail
+from .report import Report
 
 
 class ProviderFixture(BaseModel):
@@ -140,6 +141,9 @@ def pytest_configure(config):
         key, value = env_var.split("=", 1)
         os.environ[key] = value
 
+    if config.getoption("--output") is not None:
+        config.pluginmanager.register(Report(config.getoption("--output")))
+
 
 def pytest_addoption(parser):
     parser.addoption(
@@ -155,6 +159,11 @@ def pytest_addoption(parser):
         action="store",
         help="Set test config file (supported format: YAML), e.g. --config=test_config.yml",
     )
+    parser.addoption(
+        "--output",
+        action="store",
+        help="Set output file for test report, e.g. --output=pytest_report.md",
+    )
     """Add custom command line options"""
     parser.addoption(
         "--env", action="append", help="Set environment variables, e.g. --env KEY=value"
diff --git a/llama_stack/providers/tests/report.py b/llama_stack/providers/tests/report.py
new file mode 100644
index 000000000..c07d7278a
--- /dev/null
+++ b/llama_stack/providers/tests/report.py
@@ -0,0 +1,200 @@
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+# All rights reserved.
+#
+# This source code is licensed under the terms described in the LICENSE file in
+# the root directory of this source tree.
+
+
+from collections import defaultdict
+from pathlib import Path
+
+import pytest
+from llama_models.datatypes import CoreModelId
+from llama_models.sku_list import all_registered_models
+from pytest import ExitCode
+
+from pytest_html.basereport import _process_outcome
+
+
+INFERENCE_APIS = ["chat_completion"]
+FUNCTIONALITIES = ["streaming", "structured_output", "tool_calling"]
+SUPPORTED_MODELS = {
+    "ollama": set(
+        [
+            CoreModelId.llama3_1_8b_instruct.value,
+            CoreModelId.llama3_1_8b_instruct.value,
+            CoreModelId.llama3_1_70b_instruct.value,
+            CoreModelId.llama3_1_70b_instruct.value,
+            CoreModelId.llama3_1_405b_instruct.value,
+            CoreModelId.llama3_1_405b_instruct.value,
+            CoreModelId.llama3_2_1b_instruct.value,
+            CoreModelId.llama3_2_1b_instruct.value,
+            CoreModelId.llama3_2_3b_instruct.value,
+            CoreModelId.llama3_2_3b_instruct.value,
+            CoreModelId.llama3_2_11b_vision_instruct.value,
+            CoreModelId.llama3_2_11b_vision_instruct.value,
+            CoreModelId.llama3_2_90b_vision_instruct.value,
+            CoreModelId.llama3_2_90b_vision_instruct.value,
+            CoreModelId.llama3_3_70b_instruct.value,
+            CoreModelId.llama_guard_3_8b.value,
+            CoreModelId.llama_guard_3_1b.value,
+        ]
+    ),
+    "fireworks": set(
+        [
+            CoreModelId.llama3_1_8b_instruct.value,
+            CoreModelId.llama3_1_70b_instruct.value,
+            CoreModelId.llama3_1_405b_instruct.value,
+            CoreModelId.llama3_2_1b_instruct.value,
+            CoreModelId.llama3_2_3b_instruct.value,
+            CoreModelId.llama3_2_11b_vision_instruct.value,
+            CoreModelId.llama3_2_90b_vision_instruct.value,
+            CoreModelId.llama3_3_70b_instruct.value,
+            CoreModelId.llama_guard_3_8b.value,
+            CoreModelId.llama_guard_3_11b_vision.value,
+        ]
+    ),
+    "together": set(
+        [
+            CoreModelId.llama3_1_8b_instruct.value,
+            CoreModelId.llama3_1_70b_instruct.value,
+            CoreModelId.llama3_1_405b_instruct.value,
+            CoreModelId.llama3_2_3b_instruct.value,
+            CoreModelId.llama3_2_11b_vision_instruct.value,
+            CoreModelId.llama3_2_90b_vision_instruct.value,
+            CoreModelId.llama3_3_70b_instruct.value,
+            CoreModelId.llama_guard_3_8b.value,
+            CoreModelId.llama_guard_3_11b_vision.value,
+        ]
+    ),
+}
+
+
+class Report:
+
+    def __init__(self, output_path):
+
+        valid_file_format = (
+            output_path.split(".")[1] in ["md", "markdown"]
+            if len(output_path.split(".")) == 2
+            else False
+        )
+        if not valid_file_format:
+            raise ValueError(
+                f"Invalid output file {output_path}. Markdown file is required"
+            )
+        self.output_path = output_path
+        self.test_data = defaultdict(dict)
+        self.inference_tests = defaultdict(dict)
+
+    @pytest.hookimpl
+    def pytest_runtest_logreport(self, report):
+        # This hook is called in several phases, including setup, call and teardown
+        # The test is considered failed / error if any of the outcomes is not "Passed"
+        outcome = _process_outcome(report)
+        data = {
+            "outcome": report.outcome,
+            "longrepr": report.longrepr,
+            "name": report.nodeid,
+        }
+        if report.nodeid not in self.test_data:
+            self.test_data[report.nodeid] = data
+        elif self.test_data[report.nodeid] != outcome and outcome != "Passed":
+            self.test_data[report.nodeid] = data
+
+    @pytest.hookimpl
+    def pytest_sessionfinish(self, session, exitstatus):
+        if exitstatus <= ExitCode.INTERRUPTED:
+            return
+        report = []
+        report.append("# Llama Stack Integration Test Results Report")
+        report.append("\n## Summary")
+        report.append("\n## Supported Models: ")
+
+        header = "| Model Descriptor |"
+        dividor = "|:---|"
+        for k in SUPPORTED_MODELS.keys():
+            header += f"{k} |"
+            dividor += ":---:|"
+
+        report.append(header)
+        report.append(dividor)
+
+        rows = []
+        for model in all_registered_models():
+            if (
+                "Instruct" not in model.core_model_id.value
+                and "Guard" not in model.core_model_id.value
+            ):
+                continue
+            row = f"| {model.core_model_id.value} |"
+            for k in SUPPORTED_MODELS.keys():
+                if model.core_model_id.value in SUPPORTED_MODELS[k]:
+                    row += " ✅ |"
+                else:
+                    row += " ❌ |"
+            rows.append(row)
+        report.extend(rows)
+
+        report.append("\n### Tests:")
+
+        for provider in SUPPORTED_MODELS.keys():
+            if provider not in self.inference_tests:
+                continue
+            report.append(f"\n #### {provider}")
+            test_table = [
+                "| Area | Model | API | Functionality Test | Status |",
+                "|:-----|:-----|:-----|:-----|:-----|",
+            ]
+            for api in INFERENCE_APIS:
+                tests = self.inference_tests[provider][api]
+                for test_nodeid in tests:
+                    row = "|{area} | {model} | {api} | {test} | {result} ".format(
+                        area="Text" if "text" in test_nodeid else "Vision",
+                        model=(
+                            "Llama-3.1-8B-Instruct"
+                            if "text" in test_nodeid
+                            else "Llama3.2-11B-Vision-Instruct"
+                        ),
+                        api=f"/{api}",
+                        test=self.get_simple_function_name(test_nodeid),
+                        result=(
+                            "✅"
+                            if self.test_data[test_nodeid]["outcome"] == "passed"
+                            else "❌"
+                        ),
+                    )
+                    test_table += [row]
+            report.extend(test_table)
+            report.append("\n")
+
+        output_file = Path(self.output_path)
+        output_file.write_text("\n".join(report))
+        print(f"\n Report generated: {output_file.absolute()}")
+
+    @pytest.hookimpl(trylast=True)
+    def pytest_collection_modifyitems(self, session, config, items):
+        for item in items:
+            inference = item.callspec.params.get("inference_stack")
+            if "inference" in item.nodeid:
+                func_name = getattr(item, "originalname", item.name)
+                for api in INFERENCE_APIS:
+                    if api in func_name:
+                        api_tests = self.inference_tests[inference].get(api, set())
+                        api_tests.add(item.nodeid)
+                        self.inference_tests[inference][api] = api_tests
+
+    def get_simple_function_name(self, nodeid):
+        """Extract function name from nodeid.
+
+        Examples:
+        - 'tests/test_math.py::test_addition' -> 'test_addition'
+        - 'tests/test_math.py::TestClass::test_method' -> test_method'
+        """
+        parts = nodeid.split("::")
+        func_name = nodeid  # Fallback to full nodeid if pattern doesn't match
+        if len(parts) == 2:  # Simple function
+            func_name = parts[1]
+        elif len(parts) == 3:  # Class method
+            func_name = parts[2]
+        return func_name.split("[")[0]

From d1f3b032c9ee9e88dabe7b7125b3e3ffb7aa36a7 Mon Sep 17 00:00:00 2001
From: Xi Yan 
Date: Thu, 16 Jan 2025 16:07:53 -0800
Subject: [PATCH 040/107] cerebras template update for memory (#792)

# What does this PR do?

- we no longer have meta-reference as memory provider, update cerebras
template


## Test Plan

```
python llama_stack/scripts/distro_codegen.py
```

## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 distributions/dependencies.json                       |  1 +
 .../distributions/self_hosted_distro/cerebras.md      |  2 +-
 llama_stack/templates/bedrock/build.yaml              |  1 -
 llama_stack/templates/bedrock/run.yaml                |  1 -
 llama_stack/templates/cerebras/build.yaml             |  5 +++--
 llama_stack/templates/cerebras/cerebras.py            |  2 +-
 llama_stack/templates/cerebras/run.yaml               | 11 ++++++++---
 llama_stack/templates/fireworks/build.yaml            |  1 -
 llama_stack/templates/fireworks/run-with-safety.yaml  |  1 -
 llama_stack/templates/fireworks/run.yaml              |  1 -
 llama_stack/templates/hf-endpoint/build.yaml          |  1 -
 .../templates/hf-endpoint/run-with-safety.yaml        |  1 -
 llama_stack/templates/hf-endpoint/run.yaml            |  1 -
 llama_stack/templates/hf-serverless/build.yaml        |  1 -
 .../templates/hf-serverless/run-with-safety.yaml      |  1 -
 llama_stack/templates/hf-serverless/run.yaml          |  1 -
 llama_stack/templates/meta-reference-gpu/build.yaml   |  1 -
 .../templates/meta-reference-gpu/run-with-safety.yaml |  1 -
 llama_stack/templates/meta-reference-gpu/run.yaml     |  1 -
 .../templates/meta-reference-quantized-gpu/build.yaml |  1 -
 .../templates/meta-reference-quantized-gpu/run.yaml   |  1 -
 llama_stack/templates/nvidia/build.yaml               |  1 -
 llama_stack/templates/nvidia/run.yaml                 |  1 -
 llama_stack/templates/ollama/build.yaml               |  1 -
 llama_stack/templates/ollama/run-with-safety.yaml     |  1 -
 llama_stack/templates/ollama/run.yaml                 |  1 -
 llama_stack/templates/remote-vllm/build.yaml          |  1 -
 .../templates/remote-vllm/run-with-safety.yaml        |  1 -
 llama_stack/templates/remote-vllm/run.yaml            |  1 -
 llama_stack/templates/tgi/build.yaml                  |  1 -
 llama_stack/templates/tgi/run-with-safety.yaml        |  1 -
 llama_stack/templates/tgi/run.yaml                    |  1 -
 llama_stack/templates/together/build.yaml             |  1 -
 llama_stack/templates/together/run-with-safety.yaml   |  1 -
 llama_stack/templates/together/run.yaml               |  1 -
 llama_stack/templates/vllm-gpu/build.yaml             |  1 -
 llama_stack/templates/vllm-gpu/run.yaml               |  1 -
 37 files changed, 14 insertions(+), 39 deletions(-)

diff --git a/distributions/dependencies.json b/distributions/dependencies.json
index ab3a367f1..d6d60ef7c 100644
--- a/distributions/dependencies.json
+++ b/distributions/dependencies.json
@@ -346,6 +346,7 @@
     "blobfile",
     "cerebras_cloud_sdk",
     "chardet",
+    "chromadb-client",
     "datasets",
     "faiss-cpu",
     "fastapi",
diff --git a/docs/source/distributions/self_hosted_distro/cerebras.md b/docs/source/distributions/self_hosted_distro/cerebras.md
index 302d121dd..22e4125bd 100644
--- a/docs/source/distributions/self_hosted_distro/cerebras.md
+++ b/docs/source/distributions/self_hosted_distro/cerebras.md
@@ -8,7 +8,7 @@ The `llamastack/distribution-cerebras` distribution consists of the following pr
 | datasetio | `remote::huggingface`, `inline::localfs` |
 | eval | `inline::meta-reference` |
 | inference | `remote::cerebras` |
-| memory | `inline::meta-reference` |
+| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
diff --git a/llama_stack/templates/bedrock/build.yaml b/llama_stack/templates/bedrock/build.yaml
index a68a8f6fc..794e54306 100644
--- a/llama_stack/templates/bedrock/build.yaml
+++ b/llama_stack/templates/bedrock/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: bedrock
 distribution_spec:
   description: Use AWS Bedrock for running LLM inference and safety
   providers:
diff --git a/llama_stack/templates/bedrock/run.yaml b/llama_stack/templates/bedrock/run.yaml
index 1d0721773..3a6922ae7 100644
--- a/llama_stack/templates/bedrock/run.yaml
+++ b/llama_stack/templates/bedrock/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: bedrock
-conda_env: bedrock
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/cerebras/build.yaml b/llama_stack/templates/cerebras/build.yaml
index 0fe568d09..9f187d3c7 100644
--- a/llama_stack/templates/cerebras/build.yaml
+++ b/llama_stack/templates/cerebras/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: cerebras
 distribution_spec:
   description: Use Cerebras for running LLM inference
   providers:
@@ -8,7 +7,9 @@ distribution_spec:
     safety:
     - inline::llama-guard
     memory:
-    - inline::meta-reference
+    - inline::faiss
+    - remote::chromadb
+    - remote::pgvector
     agents:
     - inline::meta-reference
     eval:
diff --git a/llama_stack/templates/cerebras/cerebras.py b/llama_stack/templates/cerebras/cerebras.py
index 6571170dd..17fc26632 100644
--- a/llama_stack/templates/cerebras/cerebras.py
+++ b/llama_stack/templates/cerebras/cerebras.py
@@ -27,7 +27,7 @@ def get_distribution_template() -> DistributionTemplate:
     providers = {
         "inference": ["remote::cerebras"],
         "safety": ["inline::llama-guard"],
-        "memory": ["inline::meta-reference"],
+        "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"],
         "agents": ["inline::meta-reference"],
         "eval": ["inline::meta-reference"],
         "datasetio": ["remote::huggingface", "inline::localfs"],
diff --git a/llama_stack/templates/cerebras/run.yaml b/llama_stack/templates/cerebras/run.yaml
index 42146ad4b..e0beab9cc 100644
--- a/llama_stack/templates/cerebras/run.yaml
+++ b/llama_stack/templates/cerebras/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: cerebras
-conda_env: cerebras
 apis:
 - agents
 - datasetio
@@ -26,13 +25,19 @@ providers:
     provider_type: inline::llama-guard
     config: {}
   memory:
-  - provider_id: meta-reference
-    provider_type: inline::meta-reference
+  - provider_id: faiss
+    provider_type: inline::faiss
     config:
       kvstore:
         type: sqlite
         namespace: null
         db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/cerebras}/faiss_store.db
+  - provider_id: chromadb
+    provider_type: remote::chromadb
+    config: {}
+  - provider_id: pgvector
+    provider_type: remote::pgvector
+    config: {}
   agents:
   - provider_id: meta-reference
     provider_type: inline::meta-reference
diff --git a/llama_stack/templates/fireworks/build.yaml b/llama_stack/templates/fireworks/build.yaml
index e76cc86f1..504c913bd 100644
--- a/llama_stack/templates/fireworks/build.yaml
+++ b/llama_stack/templates/fireworks/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: fireworks
 distribution_spec:
   description: Use Fireworks.AI for running LLM inference
   providers:
diff --git a/llama_stack/templates/fireworks/run-with-safety.yaml b/llama_stack/templates/fireworks/run-with-safety.yaml
index a279ab820..8fefbd98a 100644
--- a/llama_stack/templates/fireworks/run-with-safety.yaml
+++ b/llama_stack/templates/fireworks/run-with-safety.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: fireworks
-conda_env: fireworks
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/fireworks/run.yaml b/llama_stack/templates/fireworks/run.yaml
index 79fafe66c..53128f456 100644
--- a/llama_stack/templates/fireworks/run.yaml
+++ b/llama_stack/templates/fireworks/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: fireworks
-conda_env: fireworks
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/hf-endpoint/build.yaml b/llama_stack/templates/hf-endpoint/build.yaml
index c18689855..43486030e 100644
--- a/llama_stack/templates/hf-endpoint/build.yaml
+++ b/llama_stack/templates/hf-endpoint/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: hf-endpoint
 distribution_spec:
   description: Use (an external) Hugging Face Inference Endpoint for running LLM inference
   providers:
diff --git a/llama_stack/templates/hf-endpoint/run-with-safety.yaml b/llama_stack/templates/hf-endpoint/run-with-safety.yaml
index a9d895d23..6a52ca861 100644
--- a/llama_stack/templates/hf-endpoint/run-with-safety.yaml
+++ b/llama_stack/templates/hf-endpoint/run-with-safety.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: hf-endpoint
-conda_env: hf-endpoint
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/hf-endpoint/run.yaml b/llama_stack/templates/hf-endpoint/run.yaml
index e9b58c962..c019c587a 100644
--- a/llama_stack/templates/hf-endpoint/run.yaml
+++ b/llama_stack/templates/hf-endpoint/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: hf-endpoint
-conda_env: hf-endpoint
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/hf-serverless/build.yaml b/llama_stack/templates/hf-serverless/build.yaml
index a6b551e4a..e1328bd58 100644
--- a/llama_stack/templates/hf-serverless/build.yaml
+++ b/llama_stack/templates/hf-serverless/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: hf-serverless
 distribution_spec:
   description: Use (an external) Hugging Face Inference Endpoint for running LLM inference
   providers:
diff --git a/llama_stack/templates/hf-serverless/run-with-safety.yaml b/llama_stack/templates/hf-serverless/run-with-safety.yaml
index 415cec648..0a64de358 100644
--- a/llama_stack/templates/hf-serverless/run-with-safety.yaml
+++ b/llama_stack/templates/hf-serverless/run-with-safety.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: hf-serverless
-conda_env: hf-serverless
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/hf-serverless/run.yaml b/llama_stack/templates/hf-serverless/run.yaml
index ef9dedeed..f91e45fb6 100644
--- a/llama_stack/templates/hf-serverless/run.yaml
+++ b/llama_stack/templates/hf-serverless/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: hf-serverless
-conda_env: hf-serverless
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/meta-reference-gpu/build.yaml b/llama_stack/templates/meta-reference-gpu/build.yaml
index ba8413fa6..9ad7b26bf 100644
--- a/llama_stack/templates/meta-reference-gpu/build.yaml
+++ b/llama_stack/templates/meta-reference-gpu/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: meta-reference-gpu
 distribution_spec:
   description: Use Meta Reference for running LLM inference
   providers:
diff --git a/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml b/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
index 4946fdab7..591afa2be 100644
--- a/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
+++ b/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: meta-reference-gpu
-conda_env: meta-reference-gpu
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/meta-reference-gpu/run.yaml b/llama_stack/templates/meta-reference-gpu/run.yaml
index 52345f3c1..cc22a514b 100644
--- a/llama_stack/templates/meta-reference-gpu/run.yaml
+++ b/llama_stack/templates/meta-reference-gpu/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: meta-reference-gpu
-conda_env: meta-reference-gpu
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/meta-reference-quantized-gpu/build.yaml b/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
index 41ab44e38..e6b64ea1e 100644
--- a/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
+++ b/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: meta-reference-quantized-gpu
 distribution_spec:
   description: Use Meta Reference with fp8, int4 quantization for running LLM inference
   providers:
diff --git a/llama_stack/templates/meta-reference-quantized-gpu/run.yaml b/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
index 02a5bacaa..ff0affafb 100644
--- a/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
+++ b/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: meta-reference-quantized-gpu
-conda_env: meta-reference-quantized-gpu
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/nvidia/build.yaml b/llama_stack/templates/nvidia/build.yaml
index 813502ada..56124552b 100644
--- a/llama_stack/templates/nvidia/build.yaml
+++ b/llama_stack/templates/nvidia/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: nvidia
 distribution_spec:
   description: Use NVIDIA NIM for running LLM inference
   providers:
diff --git a/llama_stack/templates/nvidia/run.yaml b/llama_stack/templates/nvidia/run.yaml
index d07eb25eb..1887a55d0 100644
--- a/llama_stack/templates/nvidia/run.yaml
+++ b/llama_stack/templates/nvidia/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: nvidia
-conda_env: nvidia
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/ollama/build.yaml b/llama_stack/templates/ollama/build.yaml
index cbd9101cf..5f2e010ee 100644
--- a/llama_stack/templates/ollama/build.yaml
+++ b/llama_stack/templates/ollama/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: ollama
 distribution_spec:
   description: Use (an external) Ollama server for running LLM inference
   providers:
diff --git a/llama_stack/templates/ollama/run-with-safety.yaml b/llama_stack/templates/ollama/run-with-safety.yaml
index 0792beddd..a808590c3 100644
--- a/llama_stack/templates/ollama/run-with-safety.yaml
+++ b/llama_stack/templates/ollama/run-with-safety.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: ollama
-conda_env: ollama
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/ollama/run.yaml b/llama_stack/templates/ollama/run.yaml
index 176465299..aa7b54a87 100644
--- a/llama_stack/templates/ollama/run.yaml
+++ b/llama_stack/templates/ollama/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: ollama
-conda_env: ollama
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/remote-vllm/build.yaml b/llama_stack/templates/remote-vllm/build.yaml
index 246e53db0..2659c8190 100644
--- a/llama_stack/templates/remote-vllm/build.yaml
+++ b/llama_stack/templates/remote-vllm/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: remote-vllm
 distribution_spec:
   description: Use (an external) vLLM server for running LLM inference
   providers:
diff --git a/llama_stack/templates/remote-vllm/run-with-safety.yaml b/llama_stack/templates/remote-vllm/run-with-safety.yaml
index 1babd04ac..4bf73bbda 100644
--- a/llama_stack/templates/remote-vllm/run-with-safety.yaml
+++ b/llama_stack/templates/remote-vllm/run-with-safety.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: remote-vllm
-conda_env: remote-vllm
 apis:
 - agents
 - inference
diff --git a/llama_stack/templates/remote-vllm/run.yaml b/llama_stack/templates/remote-vllm/run.yaml
index a3a571423..1743793a8 100644
--- a/llama_stack/templates/remote-vllm/run.yaml
+++ b/llama_stack/templates/remote-vllm/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: remote-vllm
-conda_env: remote-vllm
 apis:
 - agents
 - inference
diff --git a/llama_stack/templates/tgi/build.yaml b/llama_stack/templates/tgi/build.yaml
index 399d4a616..3bcacffb0 100644
--- a/llama_stack/templates/tgi/build.yaml
+++ b/llama_stack/templates/tgi/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: tgi
 distribution_spec:
   description: Use (an external) TGI server for running LLM inference
   providers:
diff --git a/llama_stack/templates/tgi/run-with-safety.yaml b/llama_stack/templates/tgi/run-with-safety.yaml
index 4134101f6..070daedc1 100644
--- a/llama_stack/templates/tgi/run-with-safety.yaml
+++ b/llama_stack/templates/tgi/run-with-safety.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: tgi
-conda_env: tgi
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/tgi/run.yaml b/llama_stack/templates/tgi/run.yaml
index b0b78e33b..9cfba37aa 100644
--- a/llama_stack/templates/tgi/run.yaml
+++ b/llama_stack/templates/tgi/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: tgi
-conda_env: tgi
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/together/build.yaml b/llama_stack/templates/together/build.yaml
index 96f9f758e..ad970f405 100644
--- a/llama_stack/templates/together/build.yaml
+++ b/llama_stack/templates/together/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: together
 distribution_spec:
   description: Use Together.AI for running LLM inference
   providers:
diff --git a/llama_stack/templates/together/run-with-safety.yaml b/llama_stack/templates/together/run-with-safety.yaml
index c415b0ec0..4e162aab3 100644
--- a/llama_stack/templates/together/run-with-safety.yaml
+++ b/llama_stack/templates/together/run-with-safety.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: together
-conda_env: together
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/together/run.yaml b/llama_stack/templates/together/run.yaml
index ed65ded57..3c4844447 100644
--- a/llama_stack/templates/together/run.yaml
+++ b/llama_stack/templates/together/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: together
-conda_env: together
 apis:
 - agents
 - datasetio
diff --git a/llama_stack/templates/vllm-gpu/build.yaml b/llama_stack/templates/vllm-gpu/build.yaml
index 959f91d3e..e068fa97e 100644
--- a/llama_stack/templates/vllm-gpu/build.yaml
+++ b/llama_stack/templates/vllm-gpu/build.yaml
@@ -1,5 +1,4 @@
 version: '2'
-name: vllm-gpu
 distribution_spec:
   description: Use a built-in vLLM engine for running LLM inference
   providers:
diff --git a/llama_stack/templates/vllm-gpu/run.yaml b/llama_stack/templates/vllm-gpu/run.yaml
index 48ec57cfb..1cb44b052 100644
--- a/llama_stack/templates/vllm-gpu/run.yaml
+++ b/llama_stack/templates/vllm-gpu/run.yaml
@@ -1,6 +1,5 @@
 version: '2'
 image_name: vllm-gpu
-conda_env: vllm-gpu
 apis:
 - agents
 - datasetio

From 35bf6ea75ad7787a1691605058ae7b3737069de8 Mon Sep 17 00:00:00 2001
From: Botao Chen 
Date: Thu, 16 Jan 2025 16:31:13 -0800
Subject: [PATCH 041/107] Pin torchtune pkg version (#791)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

## context
This is the follow up of
https://github.com/meta-llama/llama-stack/pull/674. Since torchtune is
still in alpha stage and the apis are not guarantee backward compatible.
Pin the torchtune and torchao pkg version to avoid the latest torchtune
release breaks llama stack post training.

We will bump the version number manually after with the new pkg release
some testing

## test
ping an old torchtune pkg version (0.4.0) and the 0.4.0 was installed
Screenshot 2025-01-16 at 3 06 47 PM
---
 llama_stack/providers/registry/post_training.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llama_stack/providers/registry/post_training.py b/llama_stack/providers/registry/post_training.py
index 3c5d06c05..3bcda6508 100644
--- a/llama_stack/providers/registry/post_training.py
+++ b/llama_stack/providers/registry/post_training.py
@@ -14,7 +14,7 @@ def available_providers() -> List[ProviderSpec]:
         InlineProviderSpec(
             api=Api.post_training,
             provider_type="inline::torchtune",
-            pip_packages=["torch", "torchtune", "torchao", "numpy"],
+            pip_packages=["torch", "torchtune==0.5.0", "torchao==0.8.0", "numpy"],
             module="llama_stack.providers.inline.post_training.torchtune",
             config_class="llama_stack.providers.inline.post_training.torchtune.TorchtunePostTrainingConfig",
             api_dependencies=[

From e88faa91e2f68b0e7b9d8da566f0149e6401a0da Mon Sep 17 00:00:00 2001
From: Dinesh Yeduguru 
Date: Thu, 16 Jan 2025 16:42:25 -0800
Subject: [PATCH 042/107] fix the code execution test in sdk tests (#794)

# What does this PR do?

remove hardcoded model id for the code execution tests


Tests:

LLAMA_STACK_CONFIG="/Users/dineshyv/.llama/distributions/llamastack-fireworks/fireworks-run.yaml"
pytest -v tests/client-sdk/agents/test_agents.py -k
"test_code_execution"
---
 tests/client-sdk/agents/test_agents.py | 15 +++++----------
 1 file changed, 5 insertions(+), 10 deletions(-)

diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py
index 19a4064a0..485779064 100644
--- a/tests/client-sdk/agents/test_agents.py
+++ b/tests/client-sdk/agents/test_agents.py
@@ -206,18 +206,13 @@ def test_builtin_tool_code_execution(llama_stack_client, agent_config):
     assert "Tool:code_interpreter Response" in logs_str
 
 
-def test_code_execution(llama_stack_client):
-    agent_config = AgentConfig(
-        model="meta-llama/Llama-3.1-8B-Instruct",
-        instructions="You are a helpful assistant",
-        toolgroups=[
+def test_code_execution(llama_stack_client, agent_config):
+    agent_config = {
+        **agent_config,
+        "toolgroups": [
             "builtin::code_interpreter",
         ],
-        tool_choice="required",
-        input_shields=[],
-        output_shields=[],
-        enable_session_persistence=False,
-    )
+    }
 
     codex_agent = Agent(llama_stack_client, agent_config)
     session_id = codex_agent.create_session("test-session")

From 73215460bad10622063843cf8494ead0f9dadb3d Mon Sep 17 00:00:00 2001
From: Dinesh Yeduguru 
Date: Thu, 16 Jan 2025 16:54:59 -0800
Subject: [PATCH 043/107] add default toolgroups to all providers (#795)

# What does this PR do?

Add toolgroup defs to all the distribution templates
---
 .../templates/hf-serverless/hf_serverless.py   |  1 +
 llama_stack/templates/hf-serverless/run.yaml   |  8 +++++++-
 .../meta-reference-gpu/meta_reference.py       |  1 +
 .../templates/meta-reference-gpu/run.yaml      |  8 +++++++-
 llama_stack/templates/nvidia/nvidia.py         | 18 ++++++++++++++++--
 llama_stack/templates/nvidia/run.yaml          |  8 +++++++-
 llama_stack/templates/ollama/ollama.py         |  1 +
 llama_stack/templates/ollama/run.yaml          |  8 +++++++-
 llama_stack/templates/remote-vllm/run.yaml     |  8 +++++++-
 llama_stack/templates/remote-vllm/vllm.py      |  1 +
 llama_stack/templates/tgi/run.yaml             |  8 +++++++-
 llama_stack/templates/tgi/tgi.py               |  1 +
 12 files changed, 63 insertions(+), 8 deletions(-)

diff --git a/llama_stack/templates/hf-serverless/hf_serverless.py b/llama_stack/templates/hf-serverless/hf_serverless.py
index 51e16c3db..788faa986 100644
--- a/llama_stack/templates/hf-serverless/hf_serverless.py
+++ b/llama_stack/templates/hf-serverless/hf_serverless.py
@@ -100,6 +100,7 @@ def get_distribution_template() -> DistributionTemplate:
                     "memory": [memory_provider],
                 },
                 default_models=[inference_model, embedding_model],
+                default_tool_groups=default_tool_groups,
             ),
             "run-with-safety.yaml": RunConfigSettings(
                 provider_overrides={
diff --git a/llama_stack/templates/hf-serverless/run.yaml b/llama_stack/templates/hf-serverless/run.yaml
index f91e45fb6..f04213533 100644
--- a/llama_stack/templates/hf-serverless/run.yaml
+++ b/llama_stack/templates/hf-serverless/run.yaml
@@ -104,4 +104,10 @@ memory_banks: []
 datasets: []
 scoring_fns: []
 eval_tasks: []
-tool_groups: []
+tool_groups:
+- toolgroup_id: builtin::websearch
+  provider_id: tavily-search
+- toolgroup_id: builtin::memory
+  provider_id: memory-runtime
+- toolgroup_id: builtin::code_interpreter
+  provider_id: code-interpreter
diff --git a/llama_stack/templates/meta-reference-gpu/meta_reference.py b/llama_stack/templates/meta-reference-gpu/meta_reference.py
index 1477b31ff..7364ee422 100644
--- a/llama_stack/templates/meta-reference-gpu/meta_reference.py
+++ b/llama_stack/templates/meta-reference-gpu/meta_reference.py
@@ -105,6 +105,7 @@ def get_distribution_template() -> DistributionTemplate:
                     "memory": [memory_provider],
                 },
                 default_models=[inference_model, embedding_model],
+                default_tool_groups=default_tool_groups,
             ),
             "run-with-safety.yaml": RunConfigSettings(
                 provider_overrides={
diff --git a/llama_stack/templates/meta-reference-gpu/run.yaml b/llama_stack/templates/meta-reference-gpu/run.yaml
index cc22a514b..ecde69fdf 100644
--- a/llama_stack/templates/meta-reference-gpu/run.yaml
+++ b/llama_stack/templates/meta-reference-gpu/run.yaml
@@ -105,4 +105,10 @@ memory_banks: []
 datasets: []
 scoring_fns: []
 eval_tasks: []
-tool_groups: []
+tool_groups:
+- toolgroup_id: builtin::websearch
+  provider_id: tavily-search
+- toolgroup_id: builtin::memory
+  provider_id: memory-runtime
+- toolgroup_id: builtin::code_interpreter
+  provider_id: code-interpreter
diff --git a/llama_stack/templates/nvidia/nvidia.py b/llama_stack/templates/nvidia/nvidia.py
index 9daf9c50c..cfa86dbe7 100644
--- a/llama_stack/templates/nvidia/nvidia.py
+++ b/llama_stack/templates/nvidia/nvidia.py
@@ -8,10 +8,9 @@ from pathlib import Path
 
 from llama_models.sku_list import all_registered_models
 
-from llama_stack.distribution.datatypes import ModelInput, Provider
+from llama_stack.distribution.datatypes import ModelInput, Provider, ToolGroupInput
 from llama_stack.providers.remote.inference.nvidia import NVIDIAConfig
 from llama_stack.providers.remote.inference.nvidia.nvidia import _MODEL_ALIASES
-
 from llama_stack.templates.template import DistributionTemplate, RunConfigSettings
 
 
@@ -50,6 +49,20 @@ def get_distribution_template() -> DistributionTemplate:
         )
         for m in _MODEL_ALIASES
     ]
+    default_tool_groups = [
+        ToolGroupInput(
+            toolgroup_id="builtin::websearch",
+            provider_id="tavily-search",
+        ),
+        ToolGroupInput(
+            toolgroup_id="builtin::memory",
+            provider_id="memory-runtime",
+        ),
+        ToolGroupInput(
+            toolgroup_id="builtin::code_interpreter",
+            provider_id="code-interpreter",
+        ),
+    ]
 
     return DistributionTemplate(
         name="nvidia",
@@ -65,6 +78,7 @@ def get_distribution_template() -> DistributionTemplate:
                     "inference": [inference_provider],
                 },
                 default_models=default_models,
+                default_tool_groups=default_tool_groups,
             ),
         },
         run_config_env_vars={
diff --git a/llama_stack/templates/nvidia/run.yaml b/llama_stack/templates/nvidia/run.yaml
index 1887a55d0..578f70c9d 100644
--- a/llama_stack/templates/nvidia/run.yaml
+++ b/llama_stack/templates/nvidia/run.yaml
@@ -137,4 +137,10 @@ memory_banks: []
 datasets: []
 scoring_fns: []
 eval_tasks: []
-tool_groups: []
+tool_groups:
+- toolgroup_id: builtin::websearch
+  provider_id: tavily-search
+- toolgroup_id: builtin::memory
+  provider_id: memory-runtime
+- toolgroup_id: builtin::code_interpreter
+  provider_id: code-interpreter
diff --git a/llama_stack/templates/ollama/ollama.py b/llama_stack/templates/ollama/ollama.py
index a9a23c1c4..0473f8692 100644
--- a/llama_stack/templates/ollama/ollama.py
+++ b/llama_stack/templates/ollama/ollama.py
@@ -101,6 +101,7 @@ def get_distribution_template() -> DistributionTemplate:
                     "memory": [memory_provider],
                 },
                 default_models=[inference_model, embedding_model],
+                default_tool_groups=default_tool_groups,
             ),
             "run-with-safety.yaml": RunConfigSettings(
                 provider_overrides={
diff --git a/llama_stack/templates/ollama/run.yaml b/llama_stack/templates/ollama/run.yaml
index aa7b54a87..2c69296fc 100644
--- a/llama_stack/templates/ollama/run.yaml
+++ b/llama_stack/templates/ollama/run.yaml
@@ -103,4 +103,10 @@ memory_banks: []
 datasets: []
 scoring_fns: []
 eval_tasks: []
-tool_groups: []
+tool_groups:
+- toolgroup_id: builtin::websearch
+  provider_id: tavily-search
+- toolgroup_id: builtin::memory
+  provider_id: memory-runtime
+- toolgroup_id: builtin::code_interpreter
+  provider_id: code-interpreter
diff --git a/llama_stack/templates/remote-vllm/run.yaml b/llama_stack/templates/remote-vllm/run.yaml
index 1743793a8..c35694d73 100644
--- a/llama_stack/templates/remote-vllm/run.yaml
+++ b/llama_stack/templates/remote-vllm/run.yaml
@@ -80,4 +80,10 @@ memory_banks: []
 datasets: []
 scoring_fns: []
 eval_tasks: []
-tool_groups: []
+tool_groups:
+- toolgroup_id: builtin::websearch
+  provider_id: tavily-search
+- toolgroup_id: builtin::memory
+  provider_id: memory-runtime
+- toolgroup_id: builtin::code_interpreter
+  provider_id: code-interpreter
diff --git a/llama_stack/templates/remote-vllm/vllm.py b/llama_stack/templates/remote-vllm/vllm.py
index 8693d70d3..9dcaf2414 100644
--- a/llama_stack/templates/remote-vllm/vllm.py
+++ b/llama_stack/templates/remote-vllm/vllm.py
@@ -99,6 +99,7 @@ def get_distribution_template() -> DistributionTemplate:
                     "memory": [memory_provider],
                 },
                 default_models=[inference_model, embedding_model],
+                default_tool_groups=default_tool_groups,
             ),
             "run-with-safety.yaml": RunConfigSettings(
                 provider_overrides={
diff --git a/llama_stack/templates/tgi/run.yaml b/llama_stack/templates/tgi/run.yaml
index 9cfba37aa..e9696c584 100644
--- a/llama_stack/templates/tgi/run.yaml
+++ b/llama_stack/templates/tgi/run.yaml
@@ -103,4 +103,10 @@ memory_banks: []
 datasets: []
 scoring_fns: []
 eval_tasks: []
-tool_groups: []
+tool_groups:
+- toolgroup_id: builtin::websearch
+  provider_id: tavily-search
+- toolgroup_id: builtin::memory
+  provider_id: memory-runtime
+- toolgroup_id: builtin::code_interpreter
+  provider_id: code-interpreter
diff --git a/llama_stack/templates/tgi/tgi.py b/llama_stack/templates/tgi/tgi.py
index 37ed2751b..b62e7719e 100644
--- a/llama_stack/templates/tgi/tgi.py
+++ b/llama_stack/templates/tgi/tgi.py
@@ -103,6 +103,7 @@ def get_distribution_template() -> DistributionTemplate:
                     "memory": [memory_provider],
                 },
                 default_models=[inference_model, embedding_model],
+                default_tool_groups=default_tool_groups,
             ),
             "run-with-safety.yaml": RunConfigSettings(
                 provider_overrides={

From 0fefd4390ab87551e1b83ca65bfeb979cd3df6cb Mon Sep 17 00:00:00 2001
From: Xi Yan 
Date: Thu, 16 Jan 2025 17:44:12 -0800
Subject: [PATCH 044/107] Fix tgi adapter (#796)

# What does this PR do?

- Fix TGI adapter

## Test Plan

image

- most inference working
- agent test failure due to model outputs

## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 llama_stack/providers/remote/inference/tgi/config.py | 4 ----
 llama_stack/providers/remote/inference/tgi/tgi.py    | 8 +++++++-
 tests/client-sdk/agents/test_agents.py               | 2 +-
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/llama_stack/providers/remote/inference/tgi/config.py b/llama_stack/providers/remote/inference/tgi/config.py
index f05005b25..4f690dec6 100644
--- a/llama_stack/providers/remote/inference/tgi/config.py
+++ b/llama_stack/providers/remote/inference/tgi/config.py
@@ -15,10 +15,6 @@ class TGIImplConfig(BaseModel):
     url: str = Field(
         description="The URL for the TGI serving endpoint",
     )
-    api_token: Optional[SecretStr] = Field(
-        default=None,
-        description="A bearer token if your TGI endpoint is protected.",
-    )
 
     @classmethod
     def sample_run_config(cls, url: str = "${env.TGI_URL}", **kwargs):
diff --git a/llama_stack/providers/remote/inference/tgi/tgi.py b/llama_stack/providers/remote/inference/tgi/tgi.py
index 985fd3606..7f8c9d8ab 100644
--- a/llama_stack/providers/remote/inference/tgi/tgi.py
+++ b/llama_stack/providers/remote/inference/tgi/tgi.py
@@ -128,6 +128,12 @@ class _HfAdapter(Inference, ModelsProtocolPrivate):
         fmt: ResponseFormat = None,
     ):
         options = get_sampling_options(sampling_params)
+        # TGI does not support temperature=0 when using greedy sampling
+        # We set it to 1e-3 instead, anything lower outputs garbage from TGI
+        # We can use top_p sampling strategy to specify lower temperature
+        if abs(options["temperature"]) < 1e-10:
+            options["temperature"] = 1e-3
+
         # delete key "max_tokens" from options since its not supported by the API
         options.pop("max_tokens", None)
         if fmt:
@@ -289,7 +295,7 @@ class TGIAdapter(_HfAdapter):
     async def initialize(self, config: TGIImplConfig) -> None:
         log.info(f"Initializing TGI client with url={config.url}")
         self.client = AsyncInferenceClient(
-            model=config.url, token=config.api_token.get_secret_value()
+            model=config.url,
         )
         endpoint_info = await self.client.get_endpoint_info()
         self.max_tokens = endpoint_info["max_total_tokens"]
diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py
index 485779064..f9b55b5cd 100644
--- a/tests/client-sdk/agents/test_agents.py
+++ b/tests/client-sdk/agents/test_agents.py
@@ -98,7 +98,7 @@ def agent_config(llama_stack_client):
         instructions="You are a helpful assistant",
         sampling_params={
             "strategy": {
-                "type": "greedy",
+                "type": "top_p",
                 "temperature": 1.0,
                 "top_p": 0.9,
             },

From 38009631bc6c6ebec9848312ccf52acc07f49107 Mon Sep 17 00:00:00 2001
From: Xi Yan 
Date: Thu, 16 Jan 2025 18:11:35 -0800
Subject: [PATCH 045/107] Remove llama-guard in Cerebras template & improve
 agent test (#798)

# What does this PR do?

- fix cerebras template
- fix agent test case without shields

## Test Plan

image

```
llama stack run ./llama_stack/templates/cerebras/run.yaml

LLAMA_STACK_BASE_URL="http://localhost:8321" pytest -v tests/client-sdk/ --html=report.html --self-contained-html
```

## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 llama_stack/templates/cerebras/cerebras.py | 17 ++++++++++-------
 llama_stack/templates/cerebras/run.yaml    |  9 +--------
 2 files changed, 11 insertions(+), 15 deletions(-)

diff --git a/llama_stack/templates/cerebras/cerebras.py b/llama_stack/templates/cerebras/cerebras.py
index 17fc26632..df3b55ddd 100644
--- a/llama_stack/templates/cerebras/cerebras.py
+++ b/llama_stack/templates/cerebras/cerebras.py
@@ -9,15 +9,11 @@ from pathlib import Path
 from llama_models.sku_list import all_registered_models
 
 from llama_stack.apis.models.models import ModelType
-from llama_stack.distribution.datatypes import (
-    ModelInput,
-    Provider,
-    ShieldInput,
-    ToolGroupInput,
-)
+from llama_stack.distribution.datatypes import ModelInput, Provider, ToolGroupInput
 from llama_stack.providers.inline.inference.sentence_transformers import (
     SentenceTransformersInferenceConfig,
 )
+from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig
 from llama_stack.providers.remote.inference.cerebras import CerebrasImplConfig
 from llama_stack.providers.remote.inference.cerebras.cerebras import model_aliases
 from llama_stack.templates.template import DistributionTemplate, RunConfigSettings
@@ -41,6 +37,7 @@ def get_distribution_template() -> DistributionTemplate:
         ],
     }
 
+    name = "cerebras"
     inference_provider = Provider(
         provider_id="cerebras",
         provider_type="remote::cerebras",
@@ -71,6 +68,11 @@ def get_distribution_template() -> DistributionTemplate:
             "embedding_dimension": 384,
         },
     )
+    memory_provider = Provider(
+        provider_id="faiss",
+        provider_type="inline::faiss",
+        config=FaissImplConfig.sample_run_config(f"distributions/{name}"),
+    )
     default_tool_groups = [
         ToolGroupInput(
             toolgroup_id="builtin::websearch",
@@ -98,9 +100,10 @@ def get_distribution_template() -> DistributionTemplate:
             "run.yaml": RunConfigSettings(
                 provider_overrides={
                     "inference": [inference_provider, embedding_provider],
+                    "memory": [memory_provider],
                 },
                 default_models=default_models + [embedding_model],
-                default_shields=[ShieldInput(shield_id="meta-llama/Llama-Guard-3-8B")],
+                default_shields=[],
                 default_tool_groups=default_tool_groups,
             ),
         },
diff --git a/llama_stack/templates/cerebras/run.yaml b/llama_stack/templates/cerebras/run.yaml
index e0beab9cc..bfc492bda 100644
--- a/llama_stack/templates/cerebras/run.yaml
+++ b/llama_stack/templates/cerebras/run.yaml
@@ -32,12 +32,6 @@ providers:
         type: sqlite
         namespace: null
         db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/cerebras}/faiss_store.db
-  - provider_id: chromadb
-    provider_type: remote::chromadb
-    config: {}
-  - provider_id: pgvector
-    provider_type: remote::pgvector
-    config: {}
   agents:
   - provider_id: meta-reference
     provider_type: inline::meta-reference
@@ -111,8 +105,7 @@ models:
   model_id: all-MiniLM-L6-v2
   provider_id: sentence-transformers
   model_type: embedding
-shields:
-- shield_id: meta-llama/Llama-Guard-3-8B
+shields: []
 memory_banks: []
 datasets: []
 scoring_fns: []

From cb41848a2a8704d9a9ee767f7a84267b52e15ca1 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Thu, 16 Jan 2025 18:14:26 -0800
Subject: [PATCH 046/107] disable version check optionally

---
 llama_stack/distribution/server/server.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py
index 6a0047f69..bb9ef0361 100644
--- a/llama_stack/distribution/server/server.py
+++ b/llama_stack/distribution/server/server.py
@@ -342,7 +342,8 @@ def main():
 
     app = FastAPI(lifespan=lifespan)
     app.add_middleware(TracingMiddleware)
-    app.add_middleware(ClientVersionMiddleware)
+    if not os.environ.get("LLAMA_STACK_DISABLE_VERSION_CHECK"):
+        app.add_middleware(ClientVersionMiddleware)
 
     try:
         impls = asyncio.run(construct_stack(config))

From 9f14382d82a266104825c53fbcff221676b19b64 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Thu, 16 Jan 2025 18:17:46 -0800
Subject: [PATCH 047/107] meta reference inference fixes (#797)

Miscellaneous fixes for meta reference inference

Tests for log probs dont pass because meta reference does not support
top_k > 1
---
 llama_stack/distribution/server/server.py          |  2 +-
 .../inline/inference/meta_reference/inference.py   | 14 +++++++++-----
 .../providers/utils/inference/prompt_adapter.py    |  6 ++++--
 tests/client-sdk/agents/test_agents.py             |  9 ++++++---
 tests/client-sdk/inference/test_inference.py       |  1 -
 5 files changed, 20 insertions(+), 12 deletions(-)

diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py
index bb9ef0361..8dbb193b9 100644
--- a/llama_stack/distribution/server/server.py
+++ b/llama_stack/distribution/server/server.py
@@ -263,7 +263,7 @@ class ClientVersionMiddleware:
                             error_msg = json.dumps(
                                 {
                                     "error": {
-                                        "message": f"Client version {client_version} is not compatible with server version {self.server_version}. Please upgrade your client."
+                                        "message": f"Client version {client_version} is not compatible with server version {self.server_version}. Please update your client."
                                     }
                                 }
                             ).encode()
diff --git a/llama_stack/providers/inline/inference/meta_reference/inference.py b/llama_stack/providers/inline/inference/meta_reference/inference.py
index d64d32f03..31ad6fa28 100644
--- a/llama_stack/providers/inline/inference/meta_reference/inference.py
+++ b/llama_stack/providers/inline/inference/meta_reference/inference.py
@@ -193,14 +193,14 @@ class MetaReferenceInferenceImpl(
                         ]
 
                 yield CompletionResponseStreamChunk(
-                    delta=TextDelta(text=text),
+                    delta=text,
                     stop_reason=stop_reason,
                     logprobs=logprobs if request.logprobs else None,
                 )
 
             if stop_reason is None:
                 yield CompletionResponseStreamChunk(
-                    delta=TextDelta(text=""),
+                    delta="",
                     stop_reason=StopReason.out_of_tokens,
                 )
 
@@ -223,10 +223,10 @@ class MetaReferenceInferenceImpl(
             tokenizer = self.generator.formatter.tokenizer
             for token_result in self.generator.completion(request):
                 tokens.append(token_result.token)
-
-                if token_result.token in tokenizer.stop_tokens:
-                    # not quite right semantically
+                if token_result.text == "<|eot_id|>":
                     stop_reason = StopReason.end_of_turn
+                elif token_result.text == "<|eom_id|>":
+                    stop_reason = StopReason.end_of_message
 
                 if request.logprobs:
                     assert len(token_result.logprobs) == 1
@@ -243,6 +243,10 @@ class MetaReferenceInferenceImpl(
                 stop_reason = StopReason.out_of_tokens
 
             content = self.generator.formatter.tokenizer.decode(tokens)
+            if content.endswith("<|eot_id|>"):
+                content = content[: -len("<|eot_id|>")]
+            elif content.endswith("<|eom_id|>"):
+                content = content[: -len("<|eom_id|>")]
             return CompletionResponse(
                 content=content,
                 stop_reason=stop_reason,
diff --git a/llama_stack/providers/utils/inference/prompt_adapter.py b/llama_stack/providers/utils/inference/prompt_adapter.py
index de4918f5c..7ee19fd7b 100644
--- a/llama_stack/providers/utils/inference/prompt_adapter.py
+++ b/llama_stack/providers/utils/inference/prompt_adapter.py
@@ -227,9 +227,11 @@ async def completion_request_to_prompt_model_input_info(
 def augment_content_with_response_format_prompt(response_format, content):
     if fmt_prompt := response_format_prompt(response_format):
         if isinstance(content, list):
-            return content + [fmt_prompt]
+            return content + [TextContentItem(text=fmt_prompt)]
+        elif isinstance(content, str):
+            return [TextContentItem(text=content), TextContentItem(text=fmt_prompt)]
         else:
-            return [content, fmt_prompt]
+            return [content, TextContentItem(text=fmt_prompt)]
 
     return content
 
diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py
index f9b55b5cd..d6d88a34f 100644
--- a/tests/client-sdk/agents/test_agents.py
+++ b/tests/client-sdk/agents/test_agents.py
@@ -80,7 +80,7 @@ class TestClientTool(ClientTool):
 
 
 @pytest.fixture(scope="session")
-def agent_config(llama_stack_client):
+def model_id(llama_stack_client):
     available_models = [
         model.identifier
         for model in llama_stack_client.models.list()
@@ -88,6 +88,11 @@ def agent_config(llama_stack_client):
     ]
     model_id = available_models[0]
     print(f"Using model: {model_id}")
+    return model_id
+
+
+@pytest.fixture(scope="session")
+def agent_config(llama_stack_client, model_id):
     available_shields = [
         shield.identifier for shield in llama_stack_client.shields.list()
     ]
@@ -246,10 +251,8 @@ def test_custom_tool(llama_stack_client, agent_config):
     client_tool = TestClientTool()
     agent_config = {
         **agent_config,
-        "model": "meta-llama/Llama-3.2-3B-Instruct",
         "toolgroups": ["builtin::websearch"],
         "client_tools": [client_tool.get_tool_definition()],
-        "tool_prompt_format": "python_list",
     }
 
     agent = Agent(llama_stack_client, agent_config, client_tools=(client_tool,))
diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py
index 671a37926..19314e4ab 100644
--- a/tests/client-sdk/inference/test_inference.py
+++ b/tests/client-sdk/inference/test_inference.py
@@ -229,7 +229,6 @@ def test_text_chat_completion_with_tool_calling_and_non_streaming(
     # response to be a tool call
     assert response.completion_message.content == ""
     assert response.completion_message.role == "assistant"
-    assert response.completion_message.stop_reason == "end_of_turn"
 
     assert len(response.completion_message.tool_calls) == 1
     assert response.completion_message.tool_calls[0].tool_name == "get_weather"

From b2ac29b9da188342ece5aa1981e48b51cd57ed93 Mon Sep 17 00:00:00 2001
From: Hardik Shah 
Date: Thu, 16 Jan 2025 19:27:29 -0800
Subject: [PATCH 048/107] fix provider model list test (#800)

Fixes provider tests

```
pytest -v -s -k "together or fireworks or ollama" --inference-model="meta-llama/Llama-3.1-8B-Instruct" ./llama_stack/providers/tests/inference/test_text_inference.py
```
```
...
....
llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_chat_completion_streaming[-together] PASSED
llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_chat_completion_with_tool_calling[-together] PASSED
llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_chat_completion_with_tool_calling_streaming[-together] PASSED

================ 21 passed, 6 skipped, 81 deselected, 5 warnings in 32.11s =================
```

Co-authored-by: Hardik Shah 
---
 .../providers/tests/inference/test_text_inference.py  | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/llama_stack/providers/tests/inference/test_text_inference.py b/llama_stack/providers/tests/inference/test_text_inference.py
index 037e99819..1243881b9 100644
--- a/llama_stack/providers/tests/inference/test_text_inference.py
+++ b/llama_stack/providers/tests/inference/test_text_inference.py
@@ -31,7 +31,7 @@ from llama_stack.apis.inference import (
     ToolChoice,
     UserMessage,
 )
-from llama_stack.apis.models import Model
+from llama_stack.apis.models import ListModelsResponse, Model
 
 from .utils import group_chunks
 
@@ -92,12 +92,13 @@ class TestInference:
     async def test_model_list(self, inference_model, inference_stack):
         _, models_impl = inference_stack
         response = await models_impl.list_models()
-        assert isinstance(response, list)
-        assert len(response) >= 1
-        assert all(isinstance(model, Model) for model in response)
+        assert isinstance(response, ListModelsResponse)
+        assert isinstance(response.data, list)
+        assert len(response.data) >= 1
+        assert all(isinstance(model, Model) for model in response.data)
 
         model_def = None
-        for model in response:
+        for model in response.data:
             if model.identifier == inference_model:
                 model_def = model
                 break

From 9d574f4aeea45e7bf50398d620566f209724b30f Mon Sep 17 00:00:00 2001
From: Xi Yan 
Date: Thu, 16 Jan 2025 19:32:07 -0800
Subject: [PATCH 049/107] fix playground for v1 (#799)

# What does this PR do?

- update playground callsites for v1 api changes

## Test Plan

```
cd llama_stack/distribution/ui
streamlit run app.py
```


https://github.com/user-attachments/assets/eace11c6-600a-42dc-b4e7-6948a706509f




## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 .../ui/page/distribution/datasets.py          |  6 ++--
 .../ui/page/distribution/eval_tasks.py        |  9 +++---
 .../ui/page/distribution/providers.py         | 14 ++++++---
 .../distribution/ui/page/playground/chat.py   |  2 +-
 .../distribution/ui/page/playground/rag.py    | 29 ++++++++++---------
 5 files changed, 35 insertions(+), 25 deletions(-)

diff --git a/llama_stack/distribution/ui/page/distribution/datasets.py b/llama_stack/distribution/ui/page/distribution/datasets.py
index 44e314cde..b52356522 100644
--- a/llama_stack/distribution/ui/page/distribution/datasets.py
+++ b/llama_stack/distribution/ui/page/distribution/datasets.py
@@ -14,6 +14,6 @@ def datasets():
     datasets_info = {
         d.identifier: d.to_dict() for d in llama_stack_api.client.datasets.list()
     }
-
-    selected_dataset = st.selectbox("Select a dataset", list(datasets_info.keys()))
-    st.json(datasets_info[selected_dataset], expanded=True)
+    if len(datasets_info) > 0:
+        selected_dataset = st.selectbox("Select a dataset", list(datasets_info.keys()))
+        st.json(datasets_info[selected_dataset], expanded=True)
diff --git a/llama_stack/distribution/ui/page/distribution/eval_tasks.py b/llama_stack/distribution/ui/page/distribution/eval_tasks.py
index 4957fb178..cc7912838 100644
--- a/llama_stack/distribution/ui/page/distribution/eval_tasks.py
+++ b/llama_stack/distribution/ui/page/distribution/eval_tasks.py
@@ -16,7 +16,8 @@ def eval_tasks():
         d.identifier: d.to_dict() for d in llama_stack_api.client.eval_tasks.list()
     }
 
-    selected_eval_task = st.selectbox(
-        "Select an eval task", list(eval_tasks_info.keys()), key="eval_task_inspect"
-    )
-    st.json(eval_tasks_info[selected_eval_task], expanded=True)
+    if len(eval_tasks_info) > 0:
+        selected_eval_task = st.selectbox(
+            "Select an eval task", list(eval_tasks_info.keys()), key="eval_task_inspect"
+        )
+        st.json(eval_tasks_info[selected_eval_task], expanded=True)
diff --git a/llama_stack/distribution/ui/page/distribution/providers.py b/llama_stack/distribution/ui/page/distribution/providers.py
index 69f6bd771..9aeb7f2a5 100644
--- a/llama_stack/distribution/ui/page/distribution/providers.py
+++ b/llama_stack/distribution/ui/page/distribution/providers.py
@@ -10,11 +10,17 @@ from modules.api import llama_stack_api
 
 def providers():
     st.header("🔍 API Providers")
-    apis_providers_info = llama_stack_api.client.providers.list()
-    # selected_api = st.selectbox("Select an API", list(apis_providers_info.keys()))
-    for api in apis_providers_info.keys():
+    apis_providers_lst = llama_stack_api.client.providers.list()
+    api_to_providers = {}
+    for api_provider in apis_providers_lst:
+        if api_provider.api in api_to_providers:
+            api_to_providers[api_provider.api].append(api_provider)
+        else:
+            api_to_providers[api_provider.api] = [api_provider]
+
+    for api in api_to_providers.keys():
         st.markdown(f"###### {api}")
-        st.dataframe([p.to_dict() for p in apis_providers_info[api]], width=500)
+        st.dataframe([x.to_dict() for x in api_to_providers[api]], width=500)
 
 
 providers()
diff --git a/llama_stack/distribution/ui/page/playground/chat.py b/llama_stack/distribution/ui/page/playground/chat.py
index 5d91ec819..cb9990b7c 100644
--- a/llama_stack/distribution/ui/page/playground/chat.py
+++ b/llama_stack/distribution/ui/page/playground/chat.py
@@ -121,7 +121,7 @@ if prompt := st.chat_input("Example: What is Llama Stack?"):
         if stream:
             for chunk in response:
                 if chunk.event.event_type == "progress":
-                    full_response += chunk.event.delta
+                    full_response += chunk.event.delta.text
                 message_placeholder.markdown(full_response + "▌")
             message_placeholder.markdown(full_response)
         else:
diff --git a/llama_stack/distribution/ui/page/playground/rag.py b/llama_stack/distribution/ui/page/playground/rag.py
index 3a2ba1270..11b05718d 100644
--- a/llama_stack/distribution/ui/page/playground/rag.py
+++ b/llama_stack/distribution/ui/page/playground/rag.py
@@ -44,14 +44,21 @@ def rag_chat_page():
                 ]
 
                 providers = llama_stack_api.client.providers.list()
+                memory_provider = None
+
+                for x in providers:
+                    if x.api == "memory":
+                        memory_provider = x.provider_id
+
                 llama_stack_api.client.memory_banks.register(
                     memory_bank_id=memory_bank_name,  # Use the user-provided name
                     params={
+                        "memory_bank_type": "vector",
                         "embedding_model": "all-MiniLM-L6-v2",
                         "chunk_size_in_tokens": 512,
                         "overlap_size_in_tokens": 64,
                     },
-                    provider_id=providers["memory"][0].provider_id,
+                    provider_id=memory_provider,
                 )
 
                 # insert documents using the custom bank name
@@ -69,9 +76,6 @@ def rag_chat_page():
             "Select Memory Banks",
             memory_banks,
         )
-        memory_bank_configs = [
-            {"bank_id": bank_id, "type": "vector"} for bank_id in selected_memory_banks
-        ]
 
         available_models = llama_stack_api.client.models.list()
         available_models = [
@@ -133,14 +137,13 @@ def rag_chat_page():
         sampling_params={
             "strategy": strategy,
         },
-        tools=[
-            {
-                "type": "memory",
-                "memory_bank_configs": memory_bank_configs,
-                "query_generator_config": {"type": "default", "sep": " "},
-                "max_tokens_in_context": 4096,
-                "max_chunks": 10,
-            }
+        toolgroups=[
+            dict(
+                name="builtin::memory",
+                args={
+                    "memory_bank_ids": [bank_id for bank_id in selected_memory_banks],
+                },
+            )
         ],
         tool_choice="auto",
         tool_prompt_format="json",
@@ -179,7 +182,7 @@ def rag_chat_page():
             retrieval_response = ""
             for log in EventLogger().log(response):
                 log.print()
-                if log.role == "memory_retrieval":
+                if log.role == "tool_execution":
                     retrieval_response += log.content.replace("====", "").strip()
                     retrieval_message_placeholder.info(retrieval_response)
                 else:

From c2a072911d957c2afb1043ee84191a910fb1af8e Mon Sep 17 00:00:00 2001
From: Xi Yan 
Date: Thu, 16 Jan 2025 23:11:21 -0800
Subject: [PATCH 050/107] fix eval notebook & add test to workflow (#803)

---
 .github/workflows/publish-to-test-pypi.yml    |   1 +
 .../Llama_Stack_Benchmark_Evals.ipynb         | 552 +++++++++---------
 2 files changed, 277 insertions(+), 276 deletions(-)

diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml
index 35cbc4dc3..9fe502254 100644
--- a/.github/workflows/publish-to-test-pypi.yml
+++ b/.github/workflows/publish-to-test-pypi.yml
@@ -239,5 +239,6 @@ jobs:
         pip install pytest nbval
         llama stack build --template together --image-type venv
         pytest -v -s --nbval-lax ./docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
+        pytest -v -s --nbval-lax ./docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
 
     # TODO: add trigger for integration test workflow & docker builds
diff --git a/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb b/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
index 83891b7ac..730017232 100644
--- a/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
+++ b/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
@@ -105,6 +105,7 @@
         }
       ],
       "source": [
+        "# NBVAL_SKIP\n",
         "!pip install -U llama-stack"
       ]
     },
@@ -309,12 +310,13 @@
         }
       ],
       "source": [
+        "# NBVAL_SKIP\n",
         "!llama stack build --template together --image-type venv"
       ]
     },
     {
       "cell_type": "code",
-      "execution_count": null,
+      "execution_count": 1,
       "metadata": {
         "colab": {
           "base_uri": "https://localhost:8080/"
@@ -328,7 +330,16 @@
           "name": "stdout",
           "output_type": "stream",
           "text": [
-            "Warning: `bwrap` is not available. Code interpreter tool will not work correctly.\n"
+            "Not in Google Colab environment\n",
+            "\u001b[33mWarning: `bwrap` is not available. Code interpreter tool will not work correctly.\u001b[0m\n"
+          ]
+        },
+        {
+          "name": "stderr",
+          "output_type": "stream",
+          "text": [
+            "/opt/anaconda3/envs/master/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
+            "  from .autonotebook import tqdm as notebook_tqdm\n"
           ]
         },
         {
@@ -356,63 +367,83 @@
               "- safety\n",
               "- scoring\n",
               "- telemetry\n",
-              "conda_env: together\n",
+              "- tool_runtime\n",
               "datasets: []\n",
               "docker_image: null\n",
               "eval_tasks: []\n",
               "image_name: together\n",
               "memory_banks: []\n",
               "metadata_store:\n",
-              "  db_path: /root/.llama/distributions/together/registry.db\n",
+              "  db_path: /Users/xiyan/.llama/distributions/together/registry.db\n",
               "  namespace: null\n",
               "  type: sqlite\n",
               "models:\n",
               "- metadata: {}\n",
               "  model_id: meta-llama/Llama-3.1-8B-Instruct\n",
-              "  model_type: &id001 !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
               "  - llm\n",
-              "  provider_id: null\n",
+              "  provider_id: together\n",
               "  provider_model_id: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo\n",
               "- metadata: {}\n",
               "  model_id: meta-llama/Llama-3.1-70B-Instruct\n",
-              "  model_type: *id001\n",
-              "  provider_id: null\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
               "  provider_model_id: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo\n",
               "- metadata: {}\n",
               "  model_id: meta-llama/Llama-3.1-405B-Instruct-FP8\n",
-              "  model_type: *id001\n",
-              "  provider_id: null\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
               "  provider_model_id: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\n",
               "- metadata: {}\n",
               "  model_id: meta-llama/Llama-3.2-3B-Instruct\n",
-              "  model_type: *id001\n",
-              "  provider_id: null\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
               "  provider_model_id: meta-llama/Llama-3.2-3B-Instruct-Turbo\n",
               "- metadata: {}\n",
               "  model_id: meta-llama/Llama-3.2-11B-Vision-Instruct\n",
-              "  model_type: *id001\n",
-              "  provider_id: null\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
               "  provider_model_id: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo\n",
               "- metadata: {}\n",
               "  model_id: meta-llama/Llama-3.2-90B-Vision-Instruct\n",
-              "  model_type: *id001\n",
-              "  provider_id: null\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
               "  provider_model_id: meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo\n",
               "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-3.3-70B-Instruct\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Llama-3.3-70B-Instruct-Turbo\n",
+              "- metadata: {}\n",
               "  model_id: meta-llama/Llama-Guard-3-8B\n",
-              "  model_type: *id001\n",
-              "  provider_id: null\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
               "  provider_model_id: meta-llama/Meta-Llama-Guard-3-8B\n",
               "- metadata: {}\n",
               "  model_id: meta-llama/Llama-Guard-3-11B-Vision\n",
-              "  model_type: *id001\n",
-              "  provider_id: null\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
               "  provider_model_id: meta-llama/Llama-Guard-3-11B-Vision-Turbo\n",
+              "- metadata:\n",
+              "    embedding_dimension: 384\n",
+              "  model_id: all-MiniLM-L6-v2\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - embedding\n",
+              "  provider_id: sentence-transformers\n",
+              "  provider_model_id: null\n",
               "providers:\n",
               "  agents:\n",
               "  - config:\n",
               "      persistence_store:\n",
-              "        db_path: /root/.llama/distributions/together/agents_store.db\n",
+              "        db_path: /Users/xiyan/.llama/distributions/together/agents_store.db\n",
               "        namespace: null\n",
               "        type: sqlite\n",
               "    provider_id: meta-reference\n",
@@ -430,14 +461,17 @@
               "    provider_type: inline::meta-reference\n",
               "  inference:\n",
               "  - config:\n",
-              "      api_key: 4985b03e627419b2964d34b8519ac6c4319f094d1ffb4f45514b4eb87e5427a2\n",
+              "      api_key: '********'\n",
               "      url: https://api.together.xyz/v1\n",
               "    provider_id: together\n",
               "    provider_type: remote::together\n",
+              "  - config: {}\n",
+              "    provider_id: sentence-transformers\n",
+              "    provider_type: inline::sentence-transformers\n",
               "  memory:\n",
               "  - config:\n",
               "      kvstore:\n",
-              "        db_path: /root/.llama/distributions/together/faiss_store.db\n",
+              "        db_path: /Users/xiyan/.llama/distributions/together/faiss_store.db\n",
               "        namespace: null\n",
               "        type: sqlite\n",
               "    provider_id: faiss\n",
@@ -454,22 +488,52 @@
               "    provider_id: llm-as-judge\n",
               "    provider_type: inline::llm-as-judge\n",
               "  - config:\n",
-              "      openai_api_key: ''\n",
+              "      openai_api_key: '********'\n",
               "    provider_id: braintrust\n",
               "    provider_type: inline::braintrust\n",
               "  telemetry:\n",
               "  - config:\n",
               "      service_name: llama-stack\n",
               "      sinks: sqlite\n",
-              "      sqlite_db_path: /root/.llama/distributions/together/trace_store.db\n",
+              "      sqlite_db_path: /Users/xiyan/.llama/distributions/together/trace_store.db\n",
               "    provider_id: meta-reference\n",
               "    provider_type: inline::meta-reference\n",
+              "  tool_runtime:\n",
+              "  - config:\n",
+              "      api_key: '********'\n",
+              "      max_results: 3\n",
+              "    provider_id: brave-search\n",
+              "    provider_type: remote::brave-search\n",
+              "  - config:\n",
+              "      api_key: '********'\n",
+              "      max_results: 3\n",
+              "    provider_id: tavily-search\n",
+              "    provider_type: remote::tavily-search\n",
+              "  - config: {}\n",
+              "    provider_id: code-interpreter\n",
+              "    provider_type: inline::code-interpreter\n",
+              "  - config: {}\n",
+              "    provider_id: memory-runtime\n",
+              "    provider_type: inline::memory-runtime\n",
               "scoring_fns: []\n",
               "shields:\n",
               "- params: null\n",
               "  provider_id: null\n",
               "  provider_shield_id: null\n",
               "  shield_id: meta-llama/Llama-Guard-3-8B\n",
+              "tool_groups:\n",
+              "- args: null\n",
+              "  mcp_endpoint: null\n",
+              "  provider_id: tavily-search\n",
+              "  toolgroup_id: builtin::websearch\n",
+              "- args: null\n",
+              "  mcp_endpoint: null\n",
+              "  provider_id: memory-runtime\n",
+              "  toolgroup_id: builtin::memory\n",
+              "- args: null\n",
+              "  mcp_endpoint: null\n",
+              "  provider_id: code-interpreter\n",
+              "  toolgroup_id: builtin::code_interpreter\n",
               "version: '2'\n",
               "\n",
               "
\n" @@ -484,63 +548,83 @@ "- safety\n", "- scoring\n", "- telemetry\n", - "conda_env: together\n", + "- tool_runtime\n", "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "docker_image: null\n", "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "image_name: together\n", "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "metadata_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", + " db_path: \u001b[35m/Users/xiyan/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", "models:\n", "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct\n", - " model_type: &id001 !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", " - llm\n", - " provider_id: null\n", + " provider_id: together\n", " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct-Turbo\n", "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct\n", - " model_type: *id001\n", - " provider_id: null\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct-Turbo\n", "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-FP8\n", - " model_type: *id001\n", - " provider_id: null\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-Turbo\n", "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct\n", - " model_type: *id001\n", - " provider_id: null\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct-Turbo\n", "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct\n", - " model_type: *id001\n", - " provider_id: null\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct-Turbo\n", "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct\n", - " model_type: *id001\n", - " provider_id: null\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct-Turbo\n", "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - " model_type: *id001\n", - " provider_id: null\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", " provider_model_id: meta-llama/Meta-Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision\n", - " model_type: *id001\n", - " provider_id: null\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", " provider_model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision-Turbo\n", + "- metadata:\n", + " embedding_dimension: \u001b[1;36m384\u001b[0m\n", + " model_id: all-MiniLM-L6-v2\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - embedding\n", + " provider_id: sentence-transformers\n", + " provider_model_id: null\n", "providers:\n", " agents:\n", " - config:\n", " persistence_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", + " db_path: \u001b[35m/Users/xiyan/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", " provider_id: meta-reference\n", @@ -558,14 +642,17 @@ " provider_type: inline::meta-reference\n", " inference:\n", " - config:\n", - " api_key: 4985b03e627419b2964d34b8519ac6c4319f094d1ffb4f45514b4eb87e5427a2\n", + " api_key: \u001b[32m'********'\u001b[0m\n", " url: \u001b[4;94mhttps://api.together.xyz/v1\u001b[0m\n", " provider_id: together\n", " provider_type: remote::together\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: sentence-transformers\n", + " provider_type: inline::sentence-transformers\n", " memory:\n", " - config:\n", " kvstore:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", + " db_path: \u001b[35m/Users/xiyan/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", " provider_id: faiss\n", @@ -582,58 +669,74 @@ " provider_id: llm-as-judge\n", " provider_type: inline::llm-as-judge\n", " - config:\n", - " openai_api_key: \u001b[32m''\u001b[0m\n", + " openai_api_key: \u001b[32m'********'\u001b[0m\n", " provider_id: braintrust\n", " provider_type: inlin\u001b[1;92me::b\u001b[0mraintrust\n", " telemetry:\n", " - config:\n", " service_name: llama-stack\n", " sinks: sqlite\n", - " sqlite_db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", + " sqlite_db_path: \u001b[35m/Users/xiyan/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", " provider_id: meta-reference\n", " provider_type: inline::meta-reference\n", + " tool_runtime:\n", + " - config:\n", + " api_key: \u001b[32m'********'\u001b[0m\n", + " max_results: \u001b[1;36m3\u001b[0m\n", + " provider_id: brave-search\n", + " provider_type: remot\u001b[1;92me::b\u001b[0mrave-search\n", + " - config:\n", + " api_key: \u001b[32m'********'\u001b[0m\n", + " max_results: \u001b[1;36m3\u001b[0m\n", + " provider_id: tavily-search\n", + " provider_type: remote::tavily-search\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: code-interpreter\n", + " provider_type: inlin\u001b[1;92me::c\u001b[0mode-interpreter\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: memory-runtime\n", + " provider_type: inline::memory-runtime\n", "scoring_fns: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "shields:\n", "- params: null\n", " provider_id: null\n", " provider_shield_id: null\n", " shield_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", + "tool_groups:\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: tavily-search\n", + " toolgroup_id: builtin::websearch\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: memory-runtime\n", + " toolgroup_id: builtin::memory\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: code-interpreter\n", + " toolgroup_id: builtin::code_interpreter\n", "version: \u001b[32m'2'\u001b[0m\n", "\n" ] }, "metadata": {}, "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "Model(identifier='meta-llama/Llama-3.1-405B-Instruct', metadata={}, provider_id='together', provider_resource_id='meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo', type='model', model_type='llm')" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" } ], "source": [ "import os\n", "\n", - "from google.colab import userdata\n", - "\n", - "os.environ[\"TOGETHER_API_KEY\"] = userdata.get(\"TOGETHER_API_KEY\")\n", + "try:\n", + " from google.colab import userdata\n", + " os.environ['TOGETHER_API_KEY'] = userdata.get('TOGETHER_API_KEY')\n", + " os.environ['TAVILY_SEARCH_API_KEY'] = userdata.get('TAVILY_SEARCH_API_KEY')\n", + "except ImportError:\n", + " print(\"Not in Google Colab environment\")\n", "\n", "from llama_stack.distribution.library_client import LlamaStackAsLibraryClient\n", "\n", "client = LlamaStackAsLibraryClient(\"together\")\n", - "_ = client.initialize()\n", - "\n", - "# register 405B as LLM Judge model\n", - "client.models.register(\n", - " model_id=\"meta-llama/Llama-3.1-405B-Instruct\",\n", - " provider_model_id=\"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\",\n", - " provider_id=\"together\",\n", - ")\n" + "_ = client.initialize()" ] }, { @@ -662,7 +765,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "metadata": { "id": "TC_IwIAQo4q-" }, @@ -670,12 +773,12 @@ "source": [ "name = \"llamastack/mmmu\"\n", "subset = \"Agriculture\"\n", - "split = \"dev\"\n" + "split = \"dev\"" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -788,130 +891,13 @@ }, "outputs": [ { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "feb82e061ee44283b4a46be858ef4cd7", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "README.md: 0%| | 0.00/36.0k [00:00EvaluateResponse(\n", "generations=[\n", + "│ │ {'generated_answer': 'Answer: D'},\n", "│ │ {\n", - "│ │ │ 'generated_answer': 'The Colorado potato beetle (Leptinotarsa decemlineata) is a significant pest of potatoes, causing damage to the leaves and stems of potato plants. The insect with black-colored antennae in the image is a Colorado potato beetle, which is known for its distinctive black and yellow stripes. On the other hand, the insect with tan-colored antennae is not a Colorado potato beetle and does not appear to be a pest of potatoes.\\n\\n*Answer*: B) The one with black coloured antennae'\n", + "│ │ │ 'generated_answer': 'The image shows a sunflower leaf with small, dark spots and white powdery patches. The dark spots are likely caused by a fungal pathogen, such as rust or septoria leaf spot, while the white powdery patches are likely caused by a fungal pathogen, such as powdery mildew.\\n\\nSince there are two distinct types of lesions on the leaf, it is likely that there are two different pathogens infecting the leaf.\\n\\n**Answer:** B) Two pathogens'\n", "│ │ },\n", "│ │ {\n", - "│ │ │ 'generated_answer': 'To determine the count of pathogens infecting this sunflower leaf, we need to analyze the image carefully. The image shows a sunflower leaf with several brown spots and patches on its surface. These brown spots and patches are indicative of fungal infections, which are common pathogens that affect sunflowers.\\n\\nUpon closer inspection, we can see that there are two distinct types of brown spots and patches on the leaf. One type is smaller and more circular in shape, while the other type is larger and more irregular in shape. This suggests that there may be two different pathogens infecting the leaf.\\n\\nHowever, without further information or testing, it is difficult to say for certain whether these two types of brown spots and patches are caused by different pathogens or if they are just different stages of the same infection. Therefore, based on the available information, the most likely answer is:\\n\\nAnswer: B) Two pathogens'\n", + "│ │ │ 'generated_answer': \"The question requires the identification of the reason behind the massive gum production on the trunks of grapefruit trees in Cyprus, despite appearing healthy from a distance. The correct answer can be deduced by analyzing the symptoms and considering the possible causes.\\n\\nTo determine the correct answer, let's evaluate each option:\\n\\nA) Don't know or not sure: This option is incorrect because it does not provide a specific reason for the gum production.\\n\\nB) Physiological stress: This option is also incorrect because it is too broad and does not specifically explain the gum production.\\n\\nC) Bacterial disease: This option is incorrect because bacterial diseases typically cause different symptoms such as leaf spots, blights, or wilting.\\n\\nD) Harvesting damage when cutting with knives: This option is incorrect because harvesting damage would likely cause wounds or scars on the tree, but it would not lead to massive gum production.\\n\\nE) Fungal gummosis: This option is the most likely cause of the gum production. Fungal gummosis is a common disease in citrus trees, including grapefruit, that causes the production of gum or sap on the trunks and branches. The disease is typically caused by fungi such as Phytophthora or Diplodia, which infect the tree through wounds or natural openings. The gum production is a defense mechanism by the tree to try to seal off the infection and prevent further damage.\\n\\nTherefore, the correct answer is:\\n\\nAnswer: E\"\n", "│ │ },\n", + "│ │ {'generated_answer': 'Answer: D'},\n", "│ │ {\n", - "│ │ │ 'generated_answer': 'Based on the image, the most likely reason for the massive gum production on the trunks of these grapefruit trees in Cyprus is a fungal infection. The gummosis, or the production of gum, is a common symptom of fungal diseases in citrus trees, and it can be caused by various factors such as root damage, water stress, or nutrient deficiencies. However, in this case, the presence of the gum on the trunks of the trees suggests that the cause is more likely related to a fungal infection.\\n\\nAnswer: E) Fungal gummosis'\n", - "│ │ },\n", - "│ │ {\n", - "│ │ │ 'generated_answer': 'The correct answer is D) Most viruses have a specific relationship with their vectors.\\n\\nExplanation:\\n\\n* Laboratory work with micro manipulators can mimic the transmission of viruses, but this is not the primary method of virus transmission in nature.\\n* Not all plant-feeding insects can transmit viruses; only specific species that have evolved to transmit particular viruses are capable of doing so.\\n* Similarly, not all plant viruses can be transmitted by insects; some are transmitted through other means such as mechanical transmission or nematodes.\\n* The correct assertion is that most viruses have a specific relationship with their vectors, meaning that each virus is typically transmitted by a specific type of insect or vector.\\n\\nAnswer: D'\n", - "│ │ },\n", - "│ │ {\n", - "│ │ │ 'generated_answer': \"The petioles of this rhubarb are splitting, and we need to determine which of the listed issues would not be the cause. \\n\\nFirst, let's consider physiological problems (A). Rhubarb is a hardy plant, but it can still experience physiological issues due to factors like temperature fluctuations, water stress, or nutrient deficiencies. These issues could potentially cause the petioles to split.\\n\\nNext, let's look at phytoplasma infection (B). Phytoplasmas are bacteria-like organisms that can infect plants, causing a range of symptoms including yellowing or browning of leaves, stunted growth, and distorted or split petioles. So, phytoplasma infection could also be a possible cause.\\n\\nNow, let's consider animal damage (D). Animals like rabbits, deer, or rodents might feed on the rhubarb leaves, causing damage to the petioles and potentially leading to splitting.\\n\\nFinally, let's think about bacteria (E). Bacterial infections can cause a range of symptoms in plants, including soft rot, leaf spot, and petiole splitting. So, bacteria could also be a potential cause.\\n\\nBased on this analysis, it seems that all of the listed issues could potentially cause the petioles of this rhubarb to split. Therefore, the correct answer is:\\n\\nAnswer: C\"\n", + "│ │ │ 'generated_answer': '**Causes of Splitting Petioles in Rhubarb**\\n\\nThe following factors can cause the petioles of rhubarb to split:\\n\\n* **Physiological Problems**: Issues such as water stress, nutrient deficiencies, or extreme temperatures can lead to splitting.\\n* **Phytoplasma Infection**: A bacterial infection caused by phytoplasma can lead to splitting of the petioles.\\n* **Animal Damage**: Pests like slugs, snails, or rodents can damage the plant and cause splitting.\\n* **Bacterial Infection**: Bacterial infections can also cause splitting.\\n\\nAs a result, the correct answer is:\\n\\n*Answer*: A) Physiological problems'\n", "│ │ }\n", "],\n", "scores={\n", "│ │ 'basic::regex_parser_multiple_choice_answer': ScoringResult(\n", - "│ │ │ aggregated_results={'accuracy': 0.2, 'num_correct': 1.0, 'num_total': 5.0},\n", + "│ │ │ aggregated_results={'accuracy': {'accuracy': 0.2, 'num_correct': 1.0, 'num_total': 5}},\n", "│ │ │ score_rows=[{'score': 0.0}, {'score': 0.0}, {'score': 0.0}, {'score': 1.0}, {'score': 0.0}]\n", "│ │ )\n", "}\n", @@ -987,25 +969,21 @@ "text/plain": [ "\u001b[1;35mEvaluateResponse\u001b[0m\u001b[1m(\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[33mgenerations\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'Answer: D'\u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'The Colorado potato beetle \u001b[0m\u001b[32m(\u001b[0m\u001b[32mLeptinotarsa decemlineata\u001b[0m\u001b[32m)\u001b[0m\u001b[32m is a significant pest of potatoes, causing damage to the leaves and stems of potato plants. The insect with black-colored antennae in the image is a Colorado potato beetle, which is known for its distinctive black and yellow stripes. On the other hand, the insect with tan-colored antennae is not a Colorado potato beetle and does not appear to be a pest of potatoes.\\n\\n*Answer*: B\u001b[0m\u001b[32m)\u001b[0m\u001b[32m The one with black coloured antennae'\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'The image shows a sunflower leaf with small, dark spots and white powdery patches. The dark spots are likely caused by a fungal pathogen, such as rust or septoria leaf spot, while the white powdery patches are likely caused by a fungal pathogen, such as powdery mildew.\\n\\nSince there are two distinct types of lesions on the leaf, it is likely that there are two different pathogens infecting the leaf.\\n\\n**Answer:** B\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Two pathogens'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'To determine the count of pathogens infecting this sunflower leaf, we need to analyze the image carefully. The image shows a sunflower leaf with several brown spots and patches on its surface. These brown spots and patches are indicative of fungal infections, which are common pathogens that affect sunflowers.\\n\\nUpon closer inspection, we can see that there are two distinct types of brown spots and patches on the leaf. One type is smaller and more circular in shape, while the other type is larger and more irregular in shape. This suggests that there may be two different pathogens infecting the leaf.\\n\\nHowever, without further information or testing, it is difficult to say for certain whether these two types of brown spots and patches are caused by different pathogens or if they are just different stages of the same infection. Therefore, based on the available information, the most likely answer is:\\n\\nAnswer: B\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Two pathogens'\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"The question requires the identification of the reason behind the massive gum production on the trunks of grapefruit trees in Cyprus, despite appearing healthy from a distance. The correct answer can be deduced by analyzing the symptoms and considering the possible causes.\\n\\nTo determine the correct answer, let's evaluate each option:\\n\\nA\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Don't know or not sure: This option is incorrect because it does not provide a specific reason for the gum production.\\n\\nB\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Physiological stress: This option is also incorrect because it is too broad and does not specifically explain the gum production.\\n\\nC\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Bacterial disease: This option is incorrect because bacterial diseases typically cause different symptoms such as leaf spots, blights, or wilting.\\n\\nD\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Harvesting damage when cutting with knives: This option is incorrect because harvesting damage would likely cause wounds or scars on the tree, but it would not lead to massive gum production.\\n\\nE\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Fungal gummosis: This option is the most likely cause of the gum production. Fungal gummosis is a common disease in citrus trees, including grapefruit, that causes the production of gum or sap on the trunks and branches. The disease is typically caused by fungi such as Phytophthora or Diplodia, which infect the tree through wounds or natural openings. The gum production is a defense mechanism by the tree to try to seal off the infection and prevent further damage.\\n\\nTherefore, the correct answer is:\\n\\nAnswer: E\"\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'Answer: D'\u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'Based on the image, the most likely reason for the massive gum production on the trunks of these grapefruit trees in Cyprus is a fungal infection. The gummosis, or the production of gum, is a common symptom of fungal diseases in citrus trees, and it can be caused by various factors such as root damage, water stress, or nutrient deficiencies. However, in this case, the presence of the gum on the trunks of the trees suggests that the cause is more likely related to a fungal infection.\\n\\nAnswer: E\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Fungal gummosis'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'The correct answer is D\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Most viruses have a specific relationship with their vectors.\\n\\nExplanation:\\n\\n* Laboratory work with micro manipulators can mimic the transmission of viruses, but this is not the primary method of virus transmission in nature.\\n* Not all plant-feeding insects can transmit viruses; only specific species that have evolved to transmit particular viruses are capable of doing so.\\n* Similarly, not all plant viruses can be transmitted by insects; some are transmitted through other means such as mechanical transmission or nematodes.\\n* The correct assertion is that most viruses have a specific relationship with their vectors, meaning that each virus is typically transmitted by a specific type of insect or vector.\\n\\nAnswer: D'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"The petioles of this rhubarb are splitting, and we need to determine which of the listed issues would not be the cause. \\n\\nFirst, let's consider physiological problems \u001b[0m\u001b[32m(\u001b[0m\u001b[32mA\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. Rhubarb is a hardy plant, but it can still experience physiological issues due to factors like temperature fluctuations, water stress, or nutrient deficiencies. These issues could potentially cause the petioles to split.\\n\\nNext, let's look at phytoplasma infection \u001b[0m\u001b[32m(\u001b[0m\u001b[32mB\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. Phytoplasmas are bacteria-like organisms that can infect plants, causing a range of symptoms including yellowing or browning of leaves, stunted growth, and distorted or split petioles. So, phytoplasma infection could also be a possible cause.\\n\\nNow, let's consider animal damage \u001b[0m\u001b[32m(\u001b[0m\u001b[32mD\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. Animals like rabbits, deer, or rodents might feed on the rhubarb leaves, causing damage to the petioles and potentially leading to splitting.\\n\\nFinally, let's think about bacteria \u001b[0m\u001b[32m(\u001b[0m\u001b[32mE\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. Bacterial infections can cause a range of symptoms in plants, including soft rot, leaf spot, and petiole splitting. So, bacteria could also be a potential cause.\\n\\nBased on this analysis, it seems that all of the listed issues could potentially cause the petioles of this rhubarb to split. Therefore, the correct answer is:\\n\\nAnswer: C\"\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'**Causes of Splitting Petioles in Rhubarb**\\n\\nThe following factors can cause the petioles of rhubarb to split:\\n\\n* **Physiological Problems**: Issues such as water stress, nutrient deficiencies, or extreme temperatures can lead to splitting.\\n* **Phytoplasma Infection**: A bacterial infection caused by phytoplasma can lead to splitting of the petioles.\\n* **Animal Damage**: Pests like slugs, snails, or rodents can damage the plant and cause splitting.\\n* **Bacterial Infection**: Bacterial infections can also cause splitting.\\n\\nAs a result, the correct answer is:\\n\\n*Answer*: A\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Physiological problems'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m]\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[33mscores\u001b[0m=\u001b[1m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'basic::regex_parser_multiple_choice_answer'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m0.2\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m5.0\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m0.2\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m5\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", @@ -1056,7 +1034,9 @@ " \"model\": \"meta-llama/Llama-3.2-90B-Vision-Instruct\",\n", " \"sampling_params\": {\n", " \"strategy\": {\n", - " \"type\": \"greedy\",\n", + " \"type\": \"top_p\",\n", + " \"temperature\": 1.0,\n", + " \"top_p\": 0.95,\n", " },\n", " \"max_tokens\": 4096,\n", " \"repeat_penalty\": 1.0,\n", @@ -1081,7 +1061,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "metadata": { "id": "HXmZf3Ymw-aX" }, @@ -1108,7 +1088,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": { "id": "Gc8azb4Rxr5J" }, @@ -1122,7 +1102,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1136,7 +1116,14 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 5/5 [00:48<00:00, 9.68s/it]\n" + " 0%| | 0/5 [00:00EvaluateResponse(\n", "generations=[\n", - "│ │ {'generated_answer': 'The recipient of the IEEE Frank Rosenblatt Award in 2010 was Vladimir Vapnik'},\n", + "│ │ {'generated_answer': \"I'm not sure who received the IEEE Frank Rosenblatt Award in 2010.\"},\n", + "│ │ {'generated_answer': \"I'm not aware of the information about the 2018 Jerlov Award recipient.\"},\n", "│ │ {\n", - "│ │ │ 'generated_answer': \"I am unable to verify who was awarded the Oceanography Society's Jerlov Award in 2018.\"\n", + "│ │ │ 'generated_answer': \"Radcliffe College was a women's liberal arts college in Cambridge, Massachusetts. However, it merged with Harvard University in 1977 and is now known as the Radcliffe Institute for Advanced Study at Harvard University.\"\n", "│ │ },\n", + "│ │ {'generated_answer': 'I do not have information on the Leipzig 1877 tournament.'},\n", "│ │ {\n", - "│ │ │ 'generated_answer': \"Radcliffe College was a women's liberal arts college, but it has since been integrated into Harvard University.\"\n", - "│ │ },\n", - "│ │ {\n", - "│ │ │ 'generated_answer': \"The Leipzig 1877 tournament was organized in the honor of 50th anniversary of the first chess club in Germany (the Leipzig Chess Club's) founding and of the 50th anniversary of Paul Morphy's birth\"\n", - "│ │ },\n", - "│ │ {\n", - "│ │ │ 'generated_answer': \"Karl Küchler's 1908 guidebook states that Empress Elizabeth of Austria's favorite sculpture, which was made for her villa Achilleion at Corfu, depicted 'Dying Achilles'.\"\n", + "│ │ │ 'generated_answer': \"I am unable to verify what Empress Elizabeth of Austria's favorite sculpture depicted at her villa Achilleion at Corfu, according to Karl Küchler.\"\n", "│ │ }\n", "],\n", "scores={\n", "│ │ 'llm-as-judge::405b-simpleqa': ScoringResult(\n", "│ │ │ aggregated_results={},\n", "│ │ │ score_rows=[\n", - "│ │ │ │ {'score': 'B', 'judge_feedback': 'B'},\n", + "│ │ │ │ {'score': 'C', 'judge_feedback': 'C'},\n", "│ │ │ │ {'score': 'C', 'judge_feedback': 'C'},\n", "│ │ │ │ {'score': 'A', 'judge_feedback': 'A'},\n", - "│ │ │ │ {'score': 'B', 'judge_feedback': 'B'},\n", - "│ │ │ │ {'score': 'B', 'judge_feedback': 'B'}\n", + "│ │ │ │ {'score': 'C', 'judge_feedback': 'C'},\n", + "│ │ │ │ {'score': 'C', 'judge_feedback': 'C'}\n", "│ │ │ ]\n", "│ │ )\n", "}\n", @@ -1176,29 +1159,25 @@ "text/plain": [ "\u001b[1;35mEvaluateResponse\u001b[0m\u001b[1m(\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[33mgenerations\u001b[0m=\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'The recipient of the IEEE Frank Rosenblatt Award in 2010 was Vladimir Vapnik'\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"I'm not sure who received the IEEE Frank Rosenblatt Award in 2010.\"\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"I'm not aware of the information about the 2018 Jerlov Award recipient.\"\u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"I am unable to verify who was awarded the Oceanography Society's Jerlov Award in 2018.\"\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"Radcliffe College was a women's liberal arts college in Cambridge, Massachusetts. However, it merged with Harvard University in 1977 and is now known as the Radcliffe Institute for Advanced Study at Harvard University.\"\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'I do not have information on the Leipzig 1877 tournament.'\u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"Radcliffe College was a women's liberal arts college, but it has since been integrated into Harvard University.\"\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"The Leipzig 1877 tournament was organized in the honor of 50th anniversary of the first chess club in Germany \u001b[0m\u001b[32m(\u001b[0m\u001b[32mthe Leipzig Chess Club's\u001b[0m\u001b[32m)\u001b[0m\u001b[32m founding and of the 50th anniversary of Paul Morphy's birth\"\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"Karl Küchler's 1908 guidebook states that Empress Elizabeth of Austria's favorite sculpture, which was made for her villa Achilleion at Corfu, depicted 'Dying Achilles'.\"\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"I am unable to verify what Empress Elizabeth of Austria's favorite sculpture depicted at her villa Achilleion at Corfu, according to Karl Küchler.\"\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m}\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m]\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[33mscores\u001b[0m=\u001b[1m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'llm-as-judge::405b-simpleqa'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'B'\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'C'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'C'\u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'C'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'C'\u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'A'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'A'\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'B'\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'B'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'C'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'C'\u001b[0m\u001b[1m}\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'C'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'C'\u001b[0m\u001b[1m}\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m]\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", @@ -1210,6 +1189,13 @@ } ], "source": [ + "# register 405B as LLM Judge model\n", + "client.models.register(\n", + " model_id=\"meta-llama/Llama-3.1-405B-Instruct\",\n", + " provider_model_id=\"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\",\n", + " provider_id=\"together\",\n", + ")\n", + "\n", "client.eval_tasks.register(\n", " eval_task_id=\"meta-reference::simpleqa\",\n", " dataset_id=simpleqa_dataset_id,\n", @@ -1257,7 +1243,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 27, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1271,7 +1257,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "5it [00:26, 5.29s/it]\n" + "5it [00:06, 1.33s/it]\n" ] }, { @@ -1280,27 +1266,25 @@ "
EvaluateResponse(\n",
               "generations=[\n",
               "│   │   {\n",
-              "│   │   │   'generated_answer': \"I'm sorry but I cannot find the recipient of the IEEE Frank Rosenblatt Award in 2010.\"\n",
+              "│   │   │   'generated_answer': 'The IEEE Frank Rosenblatt Award was given to Professor John Shawe-Taylor in 2010 for his contributions to the foundations of kernel methods.'\n",
               "│   │   },\n",
               "│   │   {\n",
-              "│   │   │   'generated_answer': \"I'm not sure who was awarded the Oceanography Society's Jerlov Award in 2018. Let me search for the information.\"\n",
+              "│   │   │   'generated_answer': 'The Jerlov Award is given by The Oceanography Society to recognize outstanding contributions to the field of ocean optics. The 2018 Jerlov Award was awarded to Dr. Kendall L. Carder.'\n",
               "│   │   },\n",
               "│   │   {\n",
-              "│   │   │   'generated_answer': \"The women's liberal arts college in Cambridge, Massachusetts is called Radcliffe College. However, in 1999, it merged with Harvard University and is now known as the Radcliffe Institute for Advanced Study at Harvard University.\"\n",
+              "│   │   │   'generated_answer': \"The women's liberal arts college in Cambridge, Massachusetts is Radcliffe College. However, in 1999, Radcliffe College merged with Harvard University to form the Radcliffe Institute for Advanced Study at Harvard University. The institute is still located in Cambridge, Massachusetts, and is dedicated to supporting women's education and research.\"\n",
               "│   │   },\n",
+              "│   │   {'generated_answer': 'The Leipzig 1877 tournament was organized in honor of Adolf Anderssen.'},\n",
               "│   │   {\n",
-              "│   │   │   'generated_answer': 'The 1877 Leipzig tournament was organized in honor of Anderssen, a German chess master.'\n",
-              "│   │   },\n",
-              "│   │   {\n",
-              "│   │   │   'generated_answer': \"Empress Elizabeth of Austria's favorite sculpture, made for her villa Achilleion at Corfu, depicted Achilles.\"\n",
+              "│   │   │   'generated_answer': \"According to Karl Küchler, Empress Elizabeth of Austria's favorite sculpture, which was made for her villa Achilleion at Corfu, depicted the Dying Achilles.\"\n",
               "│   │   }\n",
               "],\n",
               "scores={\n",
               "│   │   'llm-as-judge::405b-simpleqa': ScoringResult(\n",
               "│   │   │   aggregated_results={},\n",
               "│   │   │   score_rows=[\n",
-              "│   │   │   │   {'score': 'C', 'judge_feedback': 'C.'},\n",
-              "│   │   │   │   {'score': 'C', 'judge_feedback': 'C'},\n",
+              "│   │   │   │   {'score': 'B', 'judge_feedback': 'B'},\n",
+              "│   │   │   │   {'score': 'B', 'judge_feedback': 'B'},\n",
               "│   │   │   │   {'score': 'A', 'judge_feedback': 'A'},\n",
               "│   │   │   │   {'score': 'A', 'judge_feedback': 'A'},\n",
               "│   │   │   │   {'score': 'B', 'judge_feedback': 'B'}\n",
@@ -1314,27 +1298,25 @@
               "\u001b[1;35mEvaluateResponse\u001b[0m\u001b[1m(\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mgenerations\u001b[0m=\u001b[1m[\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m{\u001b[0m\n",
-              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"I'm sorry but I cannot find the recipient of the IEEE Frank Rosenblatt Award in 2010.\"\u001b[0m\n",
+              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'The IEEE Frank Rosenblatt Award was given to Professor John Shawe-Taylor in 2010 for his contributions to the foundations of kernel methods.'\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m{\u001b[0m\n",
-              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"I'm not sure who was awarded the Oceanography Society's Jerlov Award in 2018. Let me search for the information.\"\u001b[0m\n",
+              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'The Jerlov Award is given by The Oceanography Society to recognize outstanding contributions to the field of ocean optics. The 2018 Jerlov Award was awarded to Dr. Kendall L. Carder.'\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m{\u001b[0m\n",
-              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"The women's liberal arts college in Cambridge, Massachusetts is called Radcliffe College. However, in 1999, it merged with Harvard University and is now known as the Radcliffe Institute for Advanced Study at Harvard University.\"\u001b[0m\n",
+              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"The women's liberal arts college in Cambridge, Massachusetts is Radcliffe College. However, in 1999, Radcliffe College merged with Harvard University to form the Radcliffe Institute for Advanced Study at Harvard University. The institute is still located in Cambridge, Massachusetts, and is dedicated to supporting women's education and research.\"\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'The Leipzig 1877 tournament was organized in honor of Adolf Anderssen.'\u001b[0m\u001b[1m}\u001b[0m,\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m{\u001b[0m\n",
-              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'The 1877 Leipzig tournament was organized in honor of Anderssen, a German chess master.'\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m,\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[1m{\u001b[0m\n",
-              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"Empress Elizabeth of Austria's favorite sculpture, made for her villa Achilleion at Corfu, depicted Achilles.\"\u001b[0m\n",
+              "\u001b[2;32m│   │   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"According to Karl Küchler, Empress Elizabeth of Austria's favorite sculpture, which was made for her villa Achilleion at Corfu, depicted the Dying Achilles.\"\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m}\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1m]\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mscores\u001b[0m=\u001b[1m{\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'llm-as-judge::405b-simpleqa'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n",
               "\u001b[2;32m│   │   │   \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n",
               "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\n",
-              "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'C'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'C.'\u001b[0m\u001b[1m}\u001b[0m,\n",
-              "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'C'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'C'\u001b[0m\u001b[1m}\u001b[0m,\n",
+              "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'B'\u001b[0m\u001b[1m}\u001b[0m,\n",
+              "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'B'\u001b[0m\u001b[1m}\u001b[0m,\n",
               "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'A'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'A'\u001b[0m\u001b[1m}\u001b[0m,\n",
               "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'A'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'A'\u001b[0m\u001b[1m}\u001b[0m,\n",
               "\u001b[2;32m│   │   │   │   \u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m, \u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'B'\u001b[0m\u001b[1m}\u001b[0m\n",
@@ -1350,15 +1332,17 @@
       ],
       "source": [
         "agent_config = {\n",
-        "    \"model\": \"meta-llama/Llama-3.1-405B-Instruct\",\n",
-        "    \"instructions\": \"You are a helpful assistant\",\n",
-        "    \"sampling_params\": {\"strategy\": {\"type\": \"greedy\"}},\n",
-        "    \"tools\": [\n",
-        "        {\n",
-        "            \"type\": \"brave_search\",\n",
-        "            \"engine\": \"tavily\",\n",
-        "            \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\"),\n",
+        "    \"model\": \"meta-llama/Llama-3.3-70B-Instruct\",\n",
+        "    \"instructions\": \"You are a helpful assistant that have access to tool to search the web. \",\n",
+        "    \"sampling_params\": {\n",
+        "        \"strategy\": {\n",
+        "            \"type\": \"top_p\",\n",
+        "            \"temperature\": 0.5,\n",
+        "            \"top_p\": 0.9,\n",
         "        }\n",
+        "    },\n",
+        "    \"toolgroups\": [\n",
+        "        \"builtin::websearch\",\n",
         "    ],\n",
         "    \"tool_choice\": \"auto\",\n",
         "    \"tool_prompt_format\": \"json\",\n",
@@ -1381,6 +1365,13 @@
         ")\n",
         "pprint(response)\n"
       ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": []
     }
   ],
   "metadata": {
@@ -1396,7 +1387,16 @@
       "name": "python3"
     },
     "language_info": {
-      "name": "python"
+      "codemirror_mode": {
+        "name": "ipython",
+        "version": 3
+      },
+      "file_extension": ".py",
+      "mimetype": "text/x-python",
+      "name": "python",
+      "nbconvert_exporter": "python",
+      "pygments_lexer": "ipython3",
+      "version": "3.10.16"
     },
     "widgets": {
       "application/vnd.jupyter.widget-state+json": {

From 53b5f6b24abdc8906fe2cb44352c229e83574b07 Mon Sep 17 00:00:00 2001
From: Dinesh Yeduguru 
Date: Fri, 17 Jan 2025 11:02:25 -0800
Subject: [PATCH 051/107] add json_schema_type to ParamType deps (#808)

# What does this PR do?

Add missing json_schema_type annotation to ParamType deps
---
 docs/resources/llama-stack-spec.html   | 320 +++++++++++++++----------
 docs/resources/llama-stack-spec.yaml   | 233 +++++++++++-------
 llama_stack/apis/common/type_system.py |  13 +-
 3 files changed, 355 insertions(+), 211 deletions(-)

diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html
index 38cabdd3e..459a53888 100644
--- a/docs/resources/llama-stack-spec.html
+++ b/docs/resources/llama-stack-spec.html
@@ -6067,6 +6067,76 @@
                     "step"
                 ]
             },
+            "AgentTurnInputType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "agent_turn_input",
+                        "default": "agent_turn_input"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
+            "ArrayType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "array",
+                        "default": "array"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
+            "BooleanType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "boolean",
+                        "default": "boolean"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
+            "ChatCompletionInputType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "chat_completion_input",
+                        "default": "chat_completion_input"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
+            "CompletionInputType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "completion_input",
+                        "default": "completion_input"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
             "Dataset": {
                 "type": "object",
                 "properties": {
@@ -6130,150 +6200,110 @@
                     "metadata"
                 ]
             },
+            "JsonType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "json",
+                        "default": "json"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
+            "NumberType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "number",
+                        "default": "number"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
+            "ObjectType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "object",
+                        "default": "object"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
             "ParamType": {
                 "oneOf": [
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "string",
-                                "default": "string"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/StringType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "number",
-                                "default": "number"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/NumberType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "boolean",
-                                "default": "boolean"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/BooleanType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "array",
-                                "default": "array"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/ArrayType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "object",
-                                "default": "object"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/ObjectType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "json",
-                                "default": "json"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/JsonType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "union",
-                                "default": "union"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/UnionType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "chat_completion_input",
-                                "default": "chat_completion_input"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/ChatCompletionInputType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "completion_input",
-                                "default": "completion_input"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/CompletionInputType"
                     },
                     {
-                        "type": "object",
-                        "properties": {
-                            "type": {
-                                "type": "string",
-                                "const": "agent_turn_input",
-                                "default": "agent_turn_input"
-                            }
-                        },
-                        "additionalProperties": false,
-                        "required": [
-                            "type"
-                        ]
+                        "$ref": "#/components/schemas/AgentTurnInputType"
                     }
                 ]
             },
+            "StringType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "string",
+                        "default": "string"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
+            "UnionType": {
+                "type": "object",
+                "properties": {
+                    "type": {
+                        "type": "string",
+                        "const": "union",
+                        "default": "union"
+                    }
+                },
+                "additionalProperties": false,
+                "required": [
+                    "type"
+                ]
+            },
             "EvalTask": {
                 "type": "object",
                 "properties": {
@@ -8922,6 +8952,10 @@
             "name": "AgentTool",
             "description": ""
         },
+        {
+            "name": "AgentTurnInputType",
+            "description": ""
+        },
         {
             "name": "AgentTurnResponseEvent",
             "description": "Streamed agent execution response.\n\n"
@@ -8965,6 +8999,10 @@
             "name": "AppendRowsRequest",
             "description": ""
         },
+        {
+            "name": "ArrayType",
+            "description": ""
+        },
         {
             "name": "BasicScoringFnParams",
             "description": ""
@@ -8992,6 +9030,10 @@
             "name": "BenchmarkEvalTaskConfig",
             "description": ""
         },
+        {
+            "name": "BooleanType",
+            "description": ""
+        },
         {
             "name": "BuiltinTool",
             "description": ""
@@ -9000,6 +9042,10 @@
             "name": "CancelTrainingJobRequest",
             "description": ""
         },
+        {
+            "name": "ChatCompletionInputType",
+            "description": ""
+        },
         {
             "name": "ChatCompletionRequest",
             "description": ""
@@ -9024,6 +9070,10 @@
             "name": "Checkpoint",
             "description": "Checkpoint created during training runs\n\n"
         },
+        {
+            "name": "CompletionInputType",
+            "description": ""
+        },
         {
             "name": "CompletionMessage",
             "description": ""
@@ -9166,6 +9216,10 @@
             "name": "JobStatus",
             "description": ""
         },
+        {
+            "name": "JsonType",
+            "description": ""
+        },
         {
             "name": "KeyValueMemoryBank",
             "description": ""
@@ -9283,6 +9337,14 @@
         {
             "name": "Models"
         },
+        {
+            "name": "NumberType",
+            "description": ""
+        },
+        {
+            "name": "ObjectType",
+            "description": ""
+        },
         {
             "name": "OptimizerConfig",
             "description": ""
@@ -9490,6 +9552,10 @@
             "name": "StopReason",
             "description": ""
         },
+        {
+            "name": "StringType",
+            "description": ""
+        },
         {
             "name": "StructuredLogEvent",
             "description": ""
@@ -9622,6 +9688,10 @@
             "name": "URL",
             "description": ""
         },
+        {
+            "name": "UnionType",
+            "description": ""
+        },
         {
             "name": "UnstructuredLogEvent",
             "description": ""
@@ -9682,6 +9752,7 @@
                 "AgentSessionCreateResponse",
                 "AgentStepResponse",
                 "AgentTool",
+                "AgentTurnInputType",
                 "AgentTurnResponseEvent",
                 "AgentTurnResponseStepCompletePayload",
                 "AgentTurnResponseStepProgressPayload",
@@ -9692,20 +9763,24 @@
                 "AggregationFunctionType",
                 "AppEvalTaskConfig",
                 "AppendRowsRequest",
+                "ArrayType",
                 "BasicScoringFnParams",
                 "BatchChatCompletionRequest",
                 "BatchChatCompletionResponse",
                 "BatchCompletionRequest",
                 "BatchCompletionResponse",
                 "BenchmarkEvalTaskConfig",
+                "BooleanType",
                 "BuiltinTool",
                 "CancelTrainingJobRequest",
+                "ChatCompletionInputType",
                 "ChatCompletionRequest",
                 "ChatCompletionResponse",
                 "ChatCompletionResponseEvent",
                 "ChatCompletionResponseEventType",
                 "ChatCompletionResponseStreamChunk",
                 "Checkpoint",
+                "CompletionInputType",
                 "CompletionMessage",
                 "CompletionRequest",
                 "CompletionResponse",
@@ -9737,6 +9812,7 @@
                 "InvokeToolRequest",
                 "Job",
                 "JobStatus",
+                "JsonType",
                 "KeyValueMemoryBank",
                 "KeyValueMemoryBankParams",
                 "KeywordMemoryBank",
@@ -9764,6 +9840,8 @@
                 "Model",
                 "ModelCandidate",
                 "ModelType",
+                "NumberType",
+                "ObjectType",
                 "OptimizerConfig",
                 "OptimizerType",
                 "PaginatedRowsResult",
@@ -9812,6 +9890,7 @@
                 "SpanStatus",
                 "SpanWithStatus",
                 "StopReason",
+                "StringType",
                 "StructuredLogEvent",
                 "SupervisedFineTuneRequest",
                 "SyntheticDataGenerateRequest",
@@ -9842,6 +9921,7 @@
                 "TrainingConfig",
                 "Turn",
                 "URL",
+                "UnionType",
                 "UnstructuredLogEvent",
                 "UserMessage",
                 "VectorMemoryBank",
diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml
index 75bc25e94..9aeac6db3 100644
--- a/docs/resources/llama-stack-spec.yaml
+++ b/docs/resources/llama-stack-spec.yaml
@@ -105,6 +105,16 @@ components:
         - name
         - args
         type: object
+    AgentTurnInputType:
+      additionalProperties: false
+      properties:
+        type:
+          const: agent_turn_input
+          default: agent_turn_input
+          type: string
+      required:
+      - type
+      type: object
     AgentTurnResponseEvent:
       additionalProperties: false
       properties:
@@ -290,6 +300,16 @@ components:
       - dataset_id
       - rows
       type: object
+    ArrayType:
+      additionalProperties: false
+      properties:
+        type:
+          const: array
+          default: array
+          type: string
+      required:
+      - type
+      type: object
     BasicScoringFnParams:
       additionalProperties: false
       properties:
@@ -395,6 +415,16 @@ components:
       - type
       - eval_candidate
       type: object
+    BooleanType:
+      additionalProperties: false
+      properties:
+        type:
+          const: boolean
+          default: boolean
+          type: string
+      required:
+      - type
+      type: object
     BuiltinTool:
       enum:
       - brave_search
@@ -410,6 +440,16 @@ components:
       required:
       - job_uuid
       type: object
+    ChatCompletionInputType:
+      additionalProperties: false
+      properties:
+        type:
+          const: chat_completion_input
+          default: chat_completion_input
+          type: string
+      required:
+      - type
+      type: object
     ChatCompletionRequest:
       additionalProperties: false
       properties:
@@ -492,6 +532,16 @@ components:
       type: object
     Checkpoint:
       description: Checkpoint created during training runs
+    CompletionInputType:
+      additionalProperties: false
+      properties:
+        type:
+          const: completion_input
+          default: completion_input
+          type: string
+      required:
+      - type
+      type: object
     CompletionMessage:
       additionalProperties: false
       properties:
@@ -1007,6 +1057,16 @@ components:
       - failed
       - scheduled
       type: string
+    JsonType:
+      additionalProperties: false
+      properties:
+        type:
+          const: json
+          default: json
+          type: string
+      required:
+      - type
+      type: object
     KeyValueMemoryBank:
       additionalProperties: false
       properties:
@@ -1440,6 +1500,26 @@ components:
       - llm
       - embedding
       type: string
+    NumberType:
+      additionalProperties: false
+      properties:
+        type:
+          const: number
+          default: number
+          type: string
+      required:
+      - type
+      type: object
+    ObjectType:
+      additionalProperties: false
+      properties:
+        type:
+          const: object
+          default: object
+          type: string
+      required:
+      - type
+      type: object
     OptimizerConfig:
       additionalProperties: false
       properties:
@@ -1488,96 +1568,16 @@ components:
       type: object
     ParamType:
       oneOf:
-      - additionalProperties: false
-        properties:
-          type:
-            const: string
-            default: string
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: number
-            default: number
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: boolean
-            default: boolean
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: array
-            default: array
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: object
-            default: object
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: json
-            default: json
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: union
-            default: union
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: chat_completion_input
-            default: chat_completion_input
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: completion_input
-            default: completion_input
-            type: string
-        required:
-        - type
-        type: object
-      - additionalProperties: false
-        properties:
-          type:
-            const: agent_turn_input
-            default: agent_turn_input
-            type: string
-        required:
-        - type
-        type: object
+      - $ref: '#/components/schemas/StringType'
+      - $ref: '#/components/schemas/NumberType'
+      - $ref: '#/components/schemas/BooleanType'
+      - $ref: '#/components/schemas/ArrayType'
+      - $ref: '#/components/schemas/ObjectType'
+      - $ref: '#/components/schemas/JsonType'
+      - $ref: '#/components/schemas/UnionType'
+      - $ref: '#/components/schemas/ChatCompletionInputType'
+      - $ref: '#/components/schemas/CompletionInputType'
+      - $ref: '#/components/schemas/AgentTurnInputType'
     PostTrainingJob:
       additionalProperties: false
       properties:
@@ -2479,6 +2479,16 @@ components:
       - end_of_message
       - out_of_tokens
       type: string
+    StringType:
+      additionalProperties: false
+      properties:
+        type:
+          const: string
+          default: string
+          type: string
+      required:
+      - type
+      type: object
     StructuredLogEvent:
       additionalProperties: false
       properties:
@@ -3131,6 +3141,16 @@ components:
       required:
       - uri
       type: object
+    UnionType:
+      additionalProperties: false
+      properties:
+        type:
+          const: union
+          default: union
+          type: string
+      required:
+      - type
+      type: object
     UnstructuredLogEvent:
       additionalProperties: false
       properties:
@@ -5588,6 +5608,9 @@ tags:
   name: AgentStepResponse
 - description: 
   name: AgentTool
+- description: 
+  name: AgentTurnInputType
 - description: 'Streamed agent execution response.
 
 
@@ -5624,6 +5647,8 @@ tags:
 - description: 
   name: AppendRowsRequest
+- description: 
+  name: ArrayType
 - description: 
   name: BasicScoringFnParams
@@ -5643,11 +5668,16 @@ tags:
 - description: 
   name: BenchmarkEvalTaskConfig
+- description: 
+  name: BooleanType
 - description: 
   name: BuiltinTool
 - description: 
   name: CancelTrainingJobRequest
+- description: 
+  name: ChatCompletionInputType
 - description: 
   name: ChatCompletionRequest
@@ -5676,6 +5706,9 @@ tags:
 
     '
   name: Checkpoint
+- description: 
+  name: CompletionInputType
 - description: 
   name: CompletionMessage
@@ -5770,6 +5803,8 @@ tags:
   name: Job
 - description: 
   name: JobStatus
+- description: 
+  name: JsonType
 - description: 
   name: KeyValueMemoryBank
@@ -5847,6 +5882,10 @@ tags:
 - description: 
   name: ModelType
 - name: Models
+- description: 
+  name: NumberType
+- description: 
+  name: ObjectType
 - description: 
   name: OptimizerConfig
@@ -5988,6 +6027,8 @@ tags:
   name: SpanWithStatus
 - description: 
   name: StopReason
+- description: 
+  name: StringType
 - description: 
   name: StructuredLogEvent
@@ -6080,6 +6121,8 @@ tags:
   name: Turn
 - description: 
   name: URL
+- description: 
+  name: UnionType
 - description: 
   name: UnstructuredLogEvent
@@ -6126,6 +6169,7 @@ x-tagGroups:
   - AgentSessionCreateResponse
   - AgentStepResponse
   - AgentTool
+  - AgentTurnInputType
   - AgentTurnResponseEvent
   - AgentTurnResponseStepCompletePayload
   - AgentTurnResponseStepProgressPayload
@@ -6136,20 +6180,24 @@ x-tagGroups:
   - AggregationFunctionType
   - AppEvalTaskConfig
   - AppendRowsRequest
+  - ArrayType
   - BasicScoringFnParams
   - BatchChatCompletionRequest
   - BatchChatCompletionResponse
   - BatchCompletionRequest
   - BatchCompletionResponse
   - BenchmarkEvalTaskConfig
+  - BooleanType
   - BuiltinTool
   - CancelTrainingJobRequest
+  - ChatCompletionInputType
   - ChatCompletionRequest
   - ChatCompletionResponse
   - ChatCompletionResponseEvent
   - ChatCompletionResponseEventType
   - ChatCompletionResponseStreamChunk
   - Checkpoint
+  - CompletionInputType
   - CompletionMessage
   - CompletionRequest
   - CompletionResponse
@@ -6181,6 +6229,7 @@ x-tagGroups:
   - InvokeToolRequest
   - Job
   - JobStatus
+  - JsonType
   - KeyValueMemoryBank
   - KeyValueMemoryBankParams
   - KeywordMemoryBank
@@ -6208,6 +6257,8 @@ x-tagGroups:
   - Model
   - ModelCandidate
   - ModelType
+  - NumberType
+  - ObjectType
   - OptimizerConfig
   - OptimizerType
   - PaginatedRowsResult
@@ -6256,6 +6307,7 @@ x-tagGroups:
   - SpanStatus
   - SpanWithStatus
   - StopReason
+  - StringType
   - StructuredLogEvent
   - SupervisedFineTuneRequest
   - SyntheticDataGenerateRequest
@@ -6286,6 +6338,7 @@ x-tagGroups:
   - TrainingConfig
   - Turn
   - URL
+  - UnionType
   - UnstructuredLogEvent
   - UserMessage
   - VectorMemoryBank
diff --git a/llama_stack/apis/common/type_system.py b/llama_stack/apis/common/type_system.py
index e76cfde13..fa9c5e92e 100644
--- a/llama_stack/apis/common/type_system.py
+++ b/llama_stack/apis/common/type_system.py
@@ -6,54 +6,65 @@
 
 from typing import Literal, Union
 
-from llama_models.schema_utils import register_schema
+from llama_models.schema_utils import json_schema_type, register_schema
 from pydantic import BaseModel, Field
 from typing_extensions import Annotated
 
 
+@json_schema_type
 class StringType(BaseModel):
     type: Literal["string"] = "string"
 
 
+@json_schema_type
 class NumberType(BaseModel):
     type: Literal["number"] = "number"
 
 
+@json_schema_type
 class BooleanType(BaseModel):
     type: Literal["boolean"] = "boolean"
 
 
+@json_schema_type
 class ArrayType(BaseModel):
     type: Literal["array"] = "array"
 
 
+@json_schema_type
 class ObjectType(BaseModel):
     type: Literal["object"] = "object"
 
 
+@json_schema_type
 class JsonType(BaseModel):
     type: Literal["json"] = "json"
 
 
+@json_schema_type
 class UnionType(BaseModel):
     type: Literal["union"] = "union"
 
 
+@json_schema_type
 class ChatCompletionInputType(BaseModel):
     # expects List[Message] for messages
     type: Literal["chat_completion_input"] = "chat_completion_input"
 
 
+@json_schema_type
 class CompletionInputType(BaseModel):
     # expects InterleavedTextMedia for content
     type: Literal["completion_input"] = "completion_input"
 
 
+@json_schema_type
 class AgentTurnInputType(BaseModel):
     # expects List[Message] for messages (may also include attachments?)
     type: Literal["agent_turn_input"] = "agent_turn_input"
 
 
+@json_schema_type
 class DialogType(BaseModel):
     # expects List[Message] for messages
     # this type semantically contains the output label whereas ChatCompletionInputType does not

From e1decaec9d7d88e313eed6e4a75113b909573d5a Mon Sep 17 00:00:00 2001
From: Paul McCarthy 
Date: Fri, 17 Jan 2025 19:15:55 +0000
Subject: [PATCH 052/107] Fixing small typo in quick start guide (#807)

# What does this PR do?

Fixing small typo in the quick start guide

## Before submitting

- [x] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
---
 docs/source/getting_started/index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/source/getting_started/index.md b/docs/source/getting_started/index.md
index d7c3fe9e5..602b5a635 100644
--- a/docs/source/getting_started/index.md
+++ b/docs/source/getting_started/index.md
@@ -1,6 +1,6 @@
 # Quick Start
 
-In this guide, we'll through how you can use the Llama Stack client SDK to build a simple RAG agent.
+In this guide, we'll walk through how you can use the Llama Stack client SDK to build a simple RAG agent.
 
 The most critical requirement for running the agent is running inference on the underlying Llama model. Depending on what hardware (GPUs) you have available, you have various options. We will use `Ollama` for this purpose as it is the easiest to get started with and yet robust.
 

From 1f60c0286d6af703bb7926102968cbaa5d66cf0c Mon Sep 17 00:00:00 2001
From: Aidan Do 
Date: Sat, 18 Jan 2025 09:34:29 +1100
Subject: [PATCH 053/107] cannot import name 'GreedySamplingStrategy' (#806)

# What does this PR do?

Fixes error when running an provider using openai_compat.py

```python
Traceback (most recent call last):
  File "/home/ubuntu/miniconda3/envs/llamastack-vllm/lib/python3.10/runpy.py", line 196, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/home/ubuntu/miniconda3/envs/llamastack-vllm/lib/python3.10/runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "/home/ubuntu/us-south-2/llama-stack/llama_stack/distribution/server/server.py", line 426, in 
    main()
  File "/home/ubuntu/us-south-2/llama-stack/llama_stack/distribution/server/server.py", line 349, in main
    impls = asyncio.run(construct_stack(config))
  File "/home/ubuntu/miniconda3/envs/llamastack-vllm/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/home/ubuntu/miniconda3/envs/llamastack-vllm/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/home/ubuntu/us-south-2/llama-stack/llama_stack/distribution/stack.py", line 207, in construct_stack
    impls = await resolve_impls(
  File "/home/ubuntu/us-south-2/llama-stack/llama_stack/distribution/resolver.py", line 239, in resolve_impls
    impl = await instantiate_provider(
  File "/home/ubuntu/us-south-2/llama-stack/llama_stack/distribution/resolver.py", line 330, in instantiate_provider
    impl = await fn(*args)
  File "/home/ubuntu/us-south-2/llama-stack/llama_stack/providers/remote/inference/vllm/__init__.py", line 11, in get_adapter_impl
    from .vllm import VLLMInferenceAdapter
  File "/home/ubuntu/us-south-2/llama-stack/llama_stack/providers/remote/inference/vllm/vllm.py", line 39, in 
    from llama_stack.providers.utils.inference.openai_compat import (
  File "/home/ubuntu/us-south-2/llama-stack/llama_stack/providers/utils/inference/openai_compat.py", line 11, in 
    from llama_models.llama3.api.datatypes import (
ImportError: cannot import name 'GreedySamplingStrategy' from 'llama_models.llama3.api.datatypes' (/home/ubuntu/miniconda3/envs/llamastack-vllm/lib/python3.10/site-packages/llama_models/llama3/api/datatypes.py)
++ error_handler 61
++ echo 'Error occurred in script at line: 61'
Error occurred in script at line: 61
++ exit 1
```

## Test Plan

```bash
conda create --name llamastack-vllm python=3.10
conda activate llamastack-vllm

# To sync with the current llama-models repo
pip install -e git+https://github.com/meta-llama/llama-models.git#egg=llama-models

export INFERENCE_MODEL=unsloth/Llama-3.3-70B-Instruct-bnb-4bit && \
pip install -e . && \
llama stack build --template remote-vllm --image-type conda && \
llama stack run ./distributions/remote-vllm/run.yaml \
  --port 5000 \
  --env INFERENCE_MODEL=$INFERENCE_MODEL \
  --env VLLM_URL=http://localhost:8000
```

## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [x] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 llama_stack/providers/utils/inference/openai_compat.py | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llama_stack/providers/utils/inference/openai_compat.py b/llama_stack/providers/utils/inference/openai_compat.py
index f6350ed51..127fd19f3 100644
--- a/llama_stack/providers/utils/inference/openai_compat.py
+++ b/llama_stack/providers/utils/inference/openai_compat.py
@@ -6,15 +6,15 @@
 
 from typing import AsyncGenerator, Dict, List, Optional
 
-from llama_models.llama3.api.chat_format import ChatFormat
-
-from llama_models.llama3.api.datatypes import (
+from llama_models.datatypes import (
     GreedySamplingStrategy,
     SamplingParams,
-    StopReason,
     TopKSamplingStrategy,
     TopPSamplingStrategy,
 )
+
+from llama_models.llama3.api.chat_format import ChatFormat
+from llama_models.llama3.api.datatypes import StopReason
 from pydantic import BaseModel
 
 from llama_stack.apis.common.content_types import (

From eb60f04f86a1b9da20e64f6d466f8445416a0c95 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Fri, 17 Jan 2025 15:26:53 -0800
Subject: [PATCH 054/107] optional api dependencies (#793)

Co-authored-by: Dinesh Yeduguru 
---
 llama_stack/distribution/resolver.py                     | 9 +++++++--
 llama_stack/providers/datatypes.py                       | 3 +++
 .../inline/telemetry/meta_reference/telemetry.py         | 2 +-
 llama_stack/providers/registry/telemetry.py              | 2 +-
 llama_stack/providers/utils/telemetry/dataset_mixin.py   | 3 +++
 5 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/llama_stack/distribution/resolver.py b/llama_stack/distribution/resolver.py
index d7e947a46..204555b16 100644
--- a/llama_stack/distribution/resolver.py
+++ b/llama_stack/distribution/resolver.py
@@ -145,7 +145,9 @@ async def resolve_impls(
                 log.warning(
                     f"Provider `{provider.provider_type}` for API `{api}` is deprecated and will be removed in a future release: {p.deprecation_warning}",
                 )
-            p.deps__ = [a.value for a in p.api_dependencies]
+            p.deps__ = [a.value for a in p.api_dependencies] + [
+                a.value for a in p.optional_api_dependencies
+            ]
             spec = ProviderWithSpec(
                 spec=p,
                 **(provider.model_dump()),
@@ -229,6 +231,9 @@ async def resolve_impls(
     inner_impls_by_provider_id = {f"inner-{x.value}": {} for x in router_apis}
     for api_str, provider in sorted_providers:
         deps = {a: impls[a] for a in provider.spec.api_dependencies}
+        for a in provider.spec.optional_api_dependencies:
+            if a in impls:
+                deps[a] = impls[a]
 
         inner_impls = {}
         if isinstance(provider.spec, RoutingTableProviderSpec):
@@ -265,7 +270,7 @@ def topological_sort(
                 deps.append(dep)
 
         for dep in deps:
-            if dep not in visited:
+            if dep not in visited and dep in providers_with_specs:
                 dfs((dep, providers_with_specs[dep]), visited, stack)
 
         stack.append(api_str)
diff --git a/llama_stack/providers/datatypes.py b/llama_stack/providers/datatypes.py
index ce0c9f52e..3e64a62a1 100644
--- a/llama_stack/providers/datatypes.py
+++ b/llama_stack/providers/datatypes.py
@@ -96,6 +96,9 @@ class ProviderSpec(BaseModel):
         default_factory=list,
         description="Higher-level API surfaces may depend on other providers to provide their functionality",
     )
+    optional_api_dependencies: List[Api] = Field(
+        default_factory=list,
+    )
     deprecation_warning: Optional[str] = Field(
         default=None,
         description="If this provider is deprecated, specify the warning message here",
diff --git a/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py b/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py
index 4875f8cf0..aeeed1ac0 100644
--- a/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py
+++ b/llama_stack/providers/inline/telemetry/meta_reference/telemetry.py
@@ -72,7 +72,7 @@ def is_tracing_enabled(tracer):
 class TelemetryAdapter(TelemetryDatasetMixin, Telemetry):
     def __init__(self, config: TelemetryConfig, deps: Dict[str, Any]) -> None:
         self.config = config
-        self.datasetio_api = deps[Api.datasetio]
+        self.datasetio_api = deps.get(Api.datasetio)
 
         resource = Resource.create(
             {
diff --git a/llama_stack/providers/registry/telemetry.py b/llama_stack/providers/registry/telemetry.py
index ba7e2f806..f3b41374c 100644
--- a/llama_stack/providers/registry/telemetry.py
+++ b/llama_stack/providers/registry/telemetry.py
@@ -24,7 +24,7 @@ def available_providers() -> List[ProviderSpec]:
                 "opentelemetry-sdk",
                 "opentelemetry-exporter-otlp-proto-http",
             ],
-            api_dependencies=[Api.datasetio],
+            optional_api_dependencies=[Api.datasetio],
             module="llama_stack.providers.inline.telemetry.meta_reference",
             config_class="llama_stack.providers.inline.telemetry.meta_reference.config.TelemetryConfig",
         ),
diff --git a/llama_stack/providers/utils/telemetry/dataset_mixin.py b/llama_stack/providers/utils/telemetry/dataset_mixin.py
index 6806f39aa..a2bfdcb87 100644
--- a/llama_stack/providers/utils/telemetry/dataset_mixin.py
+++ b/llama_stack/providers/utils/telemetry/dataset_mixin.py
@@ -22,6 +22,9 @@ class TelemetryDatasetMixin:
         dataset_id: str,
         max_depth: Optional[int] = None,
     ) -> None:
+        if self.datasetio_api is None:
+            raise RuntimeError("DatasetIO API not available")
+
         spans = await self.query_spans(
             attribute_filters=attribute_filters,
             attributes_to_return=attributes_to_save,

From 9d005154d7e9f4d39124f1f9b7ca089c5500b9b1 Mon Sep 17 00:00:00 2001
From: Xi Yan 
Date: Fri, 17 Jan 2025 15:34:29 -0800
Subject: [PATCH 055/107] fix vllm template (#813)

# What does this PR do?

- Fix vLLM template to resolve
https://github.com/meta-llama/llama-stack/issues/805
- Fix agents test with shields

## Test Plan

```
vllm serve meta-llama/Llama-3.1-8B-Instruct
VLLM_URL="http://localhost:8000/v1" INFERENCE_MODEL="meta-llama/Llama-3.1-8B-Instruct" llama stack run ./llama_stack/templates/remote-vllm/run.yaml
```

```
LLAMA_STACK_BASE_URL=http://localhost:8321 pytest -v ./tests/client-sdk/
```

image


- custom tool flaky due to model outputs
- /completions API not implemented

**Vision Model**
- 11B-Vision-Instruct
image


## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 distributions/dependencies.json               | 500 +++++++++---------
 .../self_hosted_distro/remote-vllm.md         |   3 +
 llama_stack/templates/remote-vllm/build.yaml  |   9 +
 .../remote-vllm/run-with-safety.yaml          |  25 +
 llama_stack/templates/remote-vllm/run.yaml    |  25 +
 llama_stack/templates/remote-vllm/vllm.py     |   3 +
 tests/client-sdk/agents/test_agents.py        |   3 +-
 7 files changed, 318 insertions(+), 250 deletions(-)

diff --git a/distributions/dependencies.json b/distributions/dependencies.json
index d6d60ef7c..c3d643695 100644
--- a/distributions/dependencies.json
+++ b/distributions/dependencies.json
@@ -1,4 +1,104 @@
 {
+  "bedrock": [
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "boto3",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "matplotlib",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
+  "fireworks": [
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "fireworks-ai",
+    "httpx",
+    "matplotlib",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
+  "hf-endpoint": [
+    "aiohttp",
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "huggingface_hub",
+    "matplotlib",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
   "hf-serverless": [
     "aiohttp",
     "aiosqlite",
@@ -33,6 +133,154 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
+  "meta-reference-gpu": [
+    "accelerate",
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "fairscale",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "lm-format-enforcer",
+    "matplotlib",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentence-transformers",
+    "sentencepiece",
+    "torch",
+    "torchvision",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "zmq",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
+  "meta-reference-quantized-gpu": [
+    "accelerate",
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "fairscale",
+    "faiss-cpu",
+    "fastapi",
+    "fbgemm-gpu",
+    "fire",
+    "httpx",
+    "lm-format-enforcer",
+    "matplotlib",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentence-transformers",
+    "sentencepiece",
+    "torch",
+    "torchao==0.5.0",
+    "torchvision",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "zmq",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
+  "ollama": [
+    "aiohttp",
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "matplotlib",
+    "nltk",
+    "numpy",
+    "ollama",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
+  "tgi": [
+    "aiohttp",
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "huggingface_hub",
+    "matplotlib",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
   "together": [
     "aiosqlite",
     "autoevals",
@@ -66,104 +314,7 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
-  "vllm-gpu": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "vllm",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
   "remote-vllm": [
-    "aiosqlite",
-    "blobfile",
-    "chardet",
-    "chromadb-client",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "fireworks": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "fireworks-ai",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "tgi": [
-    "aiohttp",
     "aiosqlite",
     "autoevals",
     "blobfile",
@@ -174,7 +325,6 @@
     "fastapi",
     "fire",
     "httpx",
-    "huggingface_hub",
     "matplotlib",
     "nltk",
     "numpy",
@@ -196,150 +346,6 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
-  "bedrock": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "boto3",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "meta-reference-gpu": [
-    "accelerate",
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "fairscale",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "lm-format-enforcer",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentence-transformers",
-    "sentencepiece",
-    "torch",
-    "torchvision",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "zmq",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "nvidia": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "meta-reference-quantized-gpu": [
-    "accelerate",
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "fairscale",
-    "faiss-cpu",
-    "fastapi",
-    "fbgemm-gpu",
-    "fire",
-    "httpx",
-    "lm-format-enforcer",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentence-transformers",
-    "sentencepiece",
-    "torch",
-    "torchao==0.5.0",
-    "torchvision",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "zmq",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
   "cerebras": [
     "aiosqlite",
     "autoevals",
@@ -373,8 +379,7 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
-  "ollama": [
-    "aiohttp",
+  "vllm-gpu": [
     "aiosqlite",
     "autoevals",
     "blobfile",
@@ -388,7 +393,6 @@
     "matplotlib",
     "nltk",
     "numpy",
-    "ollama",
     "openai",
     "opentelemetry-exporter-otlp-proto-http",
     "opentelemetry-sdk",
@@ -404,22 +408,20 @@
     "tqdm",
     "transformers",
     "uvicorn",
+    "vllm",
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
-  "hf-endpoint": [
-    "aiohttp",
+  "nvidia": [
     "aiosqlite",
     "autoevals",
     "blobfile",
     "chardet",
-    "chromadb-client",
     "datasets",
     "faiss-cpu",
     "fastapi",
     "fire",
     "httpx",
-    "huggingface_hub",
     "matplotlib",
     "nltk",
     "numpy",
diff --git a/docs/source/distributions/self_hosted_distro/remote-vllm.md b/docs/source/distributions/self_hosted_distro/remote-vllm.md
index 98d02725c..5b29c402f 100644
--- a/docs/source/distributions/self_hosted_distro/remote-vllm.md
+++ b/docs/source/distributions/self_hosted_distro/remote-vllm.md
@@ -14,9 +14,12 @@ The `llamastack/distribution-remote-vllm` distribution consists of the following
 | API | Provider(s) |
 |-----|-------------|
 | agents | `inline::meta-reference` |
+| datasetio | `remote::huggingface`, `inline::localfs` |
+| eval | `inline::meta-reference` |
 | inference | `remote::vllm` |
 | memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 | safety | `inline::llama-guard` |
+| scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
 | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
 
diff --git a/llama_stack/templates/remote-vllm/build.yaml b/llama_stack/templates/remote-vllm/build.yaml
index 2659c8190..7398ab96d 100644
--- a/llama_stack/templates/remote-vllm/build.yaml
+++ b/llama_stack/templates/remote-vllm/build.yaml
@@ -12,6 +12,15 @@ distribution_spec:
     - inline::llama-guard
     agents:
     - inline::meta-reference
+    eval:
+    - inline::meta-reference
+    datasetio:
+    - remote::huggingface
+    - inline::localfs
+    scoring:
+    - inline::basic
+    - inline::llm-as-judge
+    - inline::braintrust
     telemetry:
     - inline::meta-reference
     tool_runtime:
diff --git a/llama_stack/templates/remote-vllm/run-with-safety.yaml b/llama_stack/templates/remote-vllm/run-with-safety.yaml
index 4bf73bbda..9c030e8b2 100644
--- a/llama_stack/templates/remote-vllm/run-with-safety.yaml
+++ b/llama_stack/templates/remote-vllm/run-with-safety.yaml
@@ -2,9 +2,12 @@ version: '2'
 image_name: remote-vllm
 apis:
 - agents
+- datasetio
+- eval
 - inference
 - memory
 - safety
+- scoring
 - telemetry
 - tool_runtime
 providers:
@@ -44,6 +47,28 @@ providers:
         type: sqlite
         namespace: null
         db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/remote-vllm}/agents_store.db
+  eval:
+  - provider_id: meta-reference
+    provider_type: inline::meta-reference
+    config: {}
+  datasetio:
+  - provider_id: huggingface
+    provider_type: remote::huggingface
+    config: {}
+  - provider_id: localfs
+    provider_type: inline::localfs
+    config: {}
+  scoring:
+  - provider_id: basic
+    provider_type: inline::basic
+    config: {}
+  - provider_id: llm-as-judge
+    provider_type: inline::llm-as-judge
+    config: {}
+  - provider_id: braintrust
+    provider_type: inline::braintrust
+    config:
+      openai_api_key: ${env.OPENAI_API_KEY:}
   telemetry:
   - provider_id: meta-reference
     provider_type: inline::meta-reference
diff --git a/llama_stack/templates/remote-vllm/run.yaml b/llama_stack/templates/remote-vllm/run.yaml
index c35694d73..053b254bd 100644
--- a/llama_stack/templates/remote-vllm/run.yaml
+++ b/llama_stack/templates/remote-vllm/run.yaml
@@ -2,9 +2,12 @@ version: '2'
 image_name: remote-vllm
 apis:
 - agents
+- datasetio
+- eval
 - inference
 - memory
 - safety
+- scoring
 - telemetry
 - tool_runtime
 providers:
@@ -38,6 +41,28 @@ providers:
         type: sqlite
         namespace: null
         db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/remote-vllm}/agents_store.db
+  eval:
+  - provider_id: meta-reference
+    provider_type: inline::meta-reference
+    config: {}
+  datasetio:
+  - provider_id: huggingface
+    provider_type: remote::huggingface
+    config: {}
+  - provider_id: localfs
+    provider_type: inline::localfs
+    config: {}
+  scoring:
+  - provider_id: basic
+    provider_type: inline::basic
+    config: {}
+  - provider_id: llm-as-judge
+    provider_type: inline::llm-as-judge
+    config: {}
+  - provider_id: braintrust
+    provider_type: inline::braintrust
+    config:
+      openai_api_key: ${env.OPENAI_API_KEY:}
   telemetry:
   - provider_id: meta-reference
     provider_type: inline::meta-reference
diff --git a/llama_stack/templates/remote-vllm/vllm.py b/llama_stack/templates/remote-vllm/vllm.py
index 9dcaf2414..229d7f172 100644
--- a/llama_stack/templates/remote-vllm/vllm.py
+++ b/llama_stack/templates/remote-vllm/vllm.py
@@ -27,6 +27,9 @@ def get_distribution_template() -> DistributionTemplate:
         "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"],
         "safety": ["inline::llama-guard"],
         "agents": ["inline::meta-reference"],
+        "eval": ["inline::meta-reference"],
+        "datasetio": ["remote::huggingface", "inline::localfs"],
+        "scoring": ["inline::basic", "inline::llm-as-judge", "inline::braintrust"],
         "telemetry": ["inline::meta-reference"],
         "tool_runtime": [
             "remote::brave-search",
diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py
index d6d88a34f..bfe279e24 100644
--- a/tests/client-sdk/agents/test_agents.py
+++ b/tests/client-sdk/agents/test_agents.py
@@ -182,7 +182,8 @@ def test_builtin_tool_web_search(llama_stack_client, agent_config):
     assert "tool_execution>" in logs_str
     assert "Tool:brave_search Response:" in logs_str
     assert "mark zuckerberg" in logs_str.lower()
-    assert "No Violation" in logs_str
+    if len(agent_config["output_shields"]) > 0:
+        assert "No Violation" in logs_str
 
 
 def test_builtin_tool_code_execution(llama_stack_client, agent_config):

From 6da3053c0e7d8d924d061143506f4b1b62373ff4 Mon Sep 17 00:00:00 2001
From: Yuan Tang 
Date: Fri, 17 Jan 2025 19:37:42 -0500
Subject: [PATCH 056/107] More generic image type for OCI-compliant container
 technologies (#802)

It's a more generic term and applicable to alternatives of Docker, such
as Podman or other OCI-compliant technologies.

---------

Signed-off-by: Yuan Tang 
---
 distributions/dell-tgi/run.yaml               |  2 +-
 .../meta-reference-quantized-gpu/run.yaml     |  2 +-
 distributions/vllm-gpu/run.yaml               |  2 +-
 docs/getting_started.ipynb                    |  4 +-
 .../Llama_Stack_Benchmark_Evals.ipynb         |  4 +-
 ...Llama_Stack_Building_AI_Applications.ipynb |  4 +-
 docs/source/distributions/building_distro.md  | 22 ++++-----
 llama_stack/cli/stack/_build.py               |  6 +--
 llama_stack/cli/stack/build.py                |  4 +-
 llama_stack/cli/stack/configure.py            |  2 +-
 llama_stack/cli/stack/run.py                  |  8 ++--
 llama_stack/distribution/build.py             | 16 ++++---
 llama_stack/distribution/build_container.sh   | 46 +++++++++----------
 .../distribution/configure_container.sh       | 12 ++---
 llama_stack/distribution/datatypes.py         | 12 ++---
 llama_stack/distribution/start_container.sh   | 14 +++---
 llama_stack/providers/datatypes.py            |  8 ++--
 llama_stack/templates/bedrock/bedrock.py      |  2 +-
 llama_stack/templates/cerebras/cerebras.py    |  2 +-
 .../experimental-post-training/build.yaml     |  2 +-
 .../experimental-post-training/run.yaml       |  2 +-
 llama_stack/templates/fireworks/fireworks.py  |  2 +-
 .../templates/hf-endpoint/hf_endpoint.py      |  2 +-
 .../templates/hf-serverless/hf_serverless.py  |  2 +-
 llama_stack/templates/nvidia/nvidia.py        |  2 +-
 llama_stack/templates/ollama/ollama.py        |  2 +-
 llama_stack/templates/template.py             | 10 ++--
 llama_stack/templates/tgi/tgi.py              |  2 +-
 llama_stack/templates/together/together.py    |  2 +-
 llama_stack/templates/vllm-gpu/vllm.py        |  2 +-
 30 files changed, 102 insertions(+), 100 deletions(-)

diff --git a/distributions/dell-tgi/run.yaml b/distributions/dell-tgi/run.yaml
index 3f8a98779..cd6ddcfdf 100644
--- a/distributions/dell-tgi/run.yaml
+++ b/distributions/dell-tgi/run.yaml
@@ -1,6 +1,6 @@
 version: '2'
 image_name: local
-docker_image: null
+container_image: null
 conda_env: local
 apis:
 - shields
diff --git a/distributions/meta-reference-quantized-gpu/run.yaml b/distributions/meta-reference-quantized-gpu/run.yaml
index 19c726b09..eb631adaa 100644
--- a/distributions/meta-reference-quantized-gpu/run.yaml
+++ b/distributions/meta-reference-quantized-gpu/run.yaml
@@ -1,6 +1,6 @@
 version: '2'
 image_name: local
-docker_image: null
+container_image: null
 conda_env: local
 apis:
 - shields
diff --git a/distributions/vllm-gpu/run.yaml b/distributions/vllm-gpu/run.yaml
index f42c942a3..a75a4c451 100644
--- a/distributions/vllm-gpu/run.yaml
+++ b/distributions/vllm-gpu/run.yaml
@@ -1,6 +1,6 @@
 version: '2'
 image_name: local
-docker_image: null
+container_image: null
 conda_env: local
 apis:
 - shields
diff --git a/docs/getting_started.ipynb b/docs/getting_started.ipynb
index 921869b33..1db7c0280 100644
--- a/docs/getting_started.ipynb
+++ b/docs/getting_started.ipynb
@@ -481,7 +481,7 @@
               "- telemetry\n",
               "conda_env: together\n",
               "datasets: []\n",
-              "docker_image: null\n",
+              "container_image: null\n",
               "eval_tasks: []\n",
               "image_name: together\n",
               "memory_banks: []\n",
@@ -600,7 +600,7 @@
               "- telemetry\n",
               "conda_env: together\n",
               "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
-              "docker_image: null\n",
+              "container_image: null\n",
               "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
               "image_name: together\n",
               "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
diff --git a/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb b/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
index 730017232..a552ce69d 100644
--- a/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
+++ b/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
@@ -369,7 +369,7 @@
               "- telemetry\n",
               "- tool_runtime\n",
               "datasets: []\n",
-              "docker_image: null\n",
+              "container_image: null\n",
               "eval_tasks: []\n",
               "image_name: together\n",
               "memory_banks: []\n",
@@ -550,7 +550,7 @@
               "- telemetry\n",
               "- tool_runtime\n",
               "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
-              "docker_image: null\n",
+              "container_image: null\n",
               "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
               "image_name: together\n",
               "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
index bed1aa2a8..df8995fd4 100644
--- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
+++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
@@ -760,7 +760,7 @@
               "- tool_runtime\n",
               "conda_env: together\n",
               "datasets: []\n",
-              "docker_image: null\n",
+              "container_image: null\n",
               "eval_tasks: []\n",
               "image_name: together\n",
               "memory_banks: []\n",
@@ -942,7 +942,7 @@
               "- tool_runtime\n",
               "conda_env: together\n",
               "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
-              "docker_image: null\n",
+              "container_image: null\n",
               "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
               "image_name: together\n",
               "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
diff --git a/docs/source/distributions/building_distro.md b/docs/source/distributions/building_distro.md
index aaf2462f7..83069aa05 100644
--- a/docs/source/distributions/building_distro.md
+++ b/docs/source/distributions/building_distro.md
@@ -17,13 +17,13 @@ pip install -e .
 llama stack build -h
 ```
 
-We will start build our distribution (in the form of a Conda environment, or Docker image). In this step, we will specify:
+We will start build our distribution (in the form of a Conda environment, or Container image). In this step, we will specify:
 - `name`: the name for our distribution (e.g. `my-stack`)
-- `image_type`: our build image type (`conda | docker`)
+- `image_type`: our build image type (`conda | container`)
 - `distribution_spec`: our distribution specs for specifying API providers
   - `description`: a short description of the configurations for the distribution
   - `providers`: specifies the underlying implementation for serving each API endpoint
-  - `image_type`: `conda` | `docker` to specify whether to build the distribution in the form of Docker image or Conda environment.
+  - `image_type`: `conda` | `container` to specify whether to build the distribution in the form of Container image or Conda environment.
 
 After this step is complete, a file named `-build.yaml` and template file `-run.yaml` will be generated and saved at the output file path specified at the end of the command.
 
@@ -35,7 +35,7 @@ After this step is complete, a file named `-build.yaml` and template file
 llama stack build
 
 > Enter a name for your Llama Stack (e.g. my-local-stack): my-stack
-> Enter the image type you want your Llama Stack to be built as (docker or conda): conda
+> Enter the image type you want your Llama Stack to be built as (container or conda): conda
 
 Llama Stack is composed of several APIs working together. Let's select
 the provider types (implementations) you want to use for these APIs.
@@ -348,26 +348,26 @@ llama stack build --config llama_stack/templates/ollama/build.yaml
 ```
 :::
 
-:::{tab-item} Building Docker
+:::{tab-item} Building Container
 > [!TIP]
-> Podman is supported as an alternative to Docker. Set `DOCKER_BINARY` to `podman` in your environment to use Podman.
+> Podman is supported as an alternative to Docker. Set `CONTAINER_BINARY` to `podman` in your environment to use Podman.
 
-To build a docker image, you may start off from a template and use the `--image-type docker` flag to specify `docker` as the build image type.
+To build a container image, you may start off from a template and use the `--image-type container` flag to specify `container` as the build image type.
 
 ```
-llama stack build --template ollama --image-type docker
+llama stack build --template ollama --image-type container
 ```
 
 ```
-$ llama stack build --template ollama --image-type docker
+$ llama stack build --template ollama --image-type container
 ...
-Dockerfile created successfully in /tmp/tmp.viA3a3Rdsg/DockerfileFROM python:3.10-slim
+Containerfile created successfully in /tmp/tmp.viA3a3Rdsg/ContainerfileFROM python:3.10-slim
 ...
 
 You can now edit ~/meta-llama/llama-stack/tmp/configs/ollama-run.yaml and run `llama stack run ~/meta-llama/llama-stack/tmp/configs/ollama-run.yaml`
 ```
 
-After this step is successful, you should be able to find the built docker image and test it with `llama stack run `.
+After this step is successful, you should be able to find the built container image and test it with `llama stack run `.
 :::
 
 ::::
diff --git a/llama_stack/cli/stack/_build.py b/llama_stack/cli/stack/_build.py
index 08c987a50..16ca670f7 100644
--- a/llama_stack/cli/stack/_build.py
+++ b/llama_stack/cli/stack/_build.py
@@ -182,8 +182,8 @@ def _generate_run_config(
     """
     apis = list(build_config.distribution_spec.providers.keys())
     run_config = StackRunConfig(
-        docker_image=(
-            image_name if build_config.image_type == ImageType.docker.value else None
+        container_image=(
+            image_name if build_config.image_type == ImageType.container.value else None
         ),
         image_name=image_name,
         apis=apis,
@@ -238,7 +238,7 @@ def _run_stack_build_command_from_build_config(
     image_name: Optional[str] = None,
     template_name: Optional[str] = None,
 ) -> None:
-    if build_config.image_type == ImageType.docker.value:
+    if build_config.image_type == ImageType.container.value:
         if template_name:
             image_name = f"distribution-{template_name}"
         else:
diff --git a/llama_stack/cli/stack/build.py b/llama_stack/cli/stack/build.py
index d00157710..48c811839 100644
--- a/llama_stack/cli/stack/build.py
+++ b/llama_stack/cli/stack/build.py
@@ -47,8 +47,8 @@ class StackBuild(Subcommand):
         self.parser.add_argument(
             "--image-type",
             type=str,
-            help="Image Type to use for the build. This can be either conda or docker. If not specified, will use the image type from the template config.",
-            choices=["conda", "docker", "venv"],
+            help="Image Type to use for the build. This can be either conda or container or venv. If not specified, will use the image type from the template config.",
+            choices=["conda", "container", "venv"],
             default="conda",
         )
 
diff --git a/llama_stack/cli/stack/configure.py b/llama_stack/cli/stack/configure.py
index 11d3f705a..56f4feceb 100644
--- a/llama_stack/cli/stack/configure.py
+++ b/llama_stack/cli/stack/configure.py
@@ -27,7 +27,7 @@ class StackConfigure(Subcommand):
         self.parser.add_argument(
             "config",
             type=str,
-            help="Path to the build config file (e.g. ~/.llama/builds//-build.yaml). For docker, this could also be the name of the docker image. ",
+            help="Path to the build config file (e.g. ~/.llama/builds//-build.yaml). For container, this could also be the name of the container image. ",
         )
 
         self.parser.add_argument(
diff --git a/llama_stack/cli/stack/run.py b/llama_stack/cli/stack/run.py
index 9fa82bd61..e1e02d10c 100644
--- a/llama_stack/cli/stack/run.py
+++ b/llama_stack/cli/stack/run.py
@@ -92,9 +92,9 @@ class StackRun(Subcommand):
             )
 
         if not config_file.exists() and not has_yaml_suffix:
-            # check if it's a build config saved to docker dir
+            # check if it's a build config saved to container dir
             config_file = Path(
-                BUILDS_BASE_DIR / ImageType.docker.value / f"{args.config}-run.yaml"
+                BUILDS_BASE_DIR / ImageType.container.value / f"{args.config}-run.yaml"
             )
 
         if not config_file.exists() and not has_yaml_suffix:
@@ -115,12 +115,12 @@ class StackRun(Subcommand):
         config_dict = yaml.safe_load(config_file.read_text())
         config = parse_and_maybe_upgrade_config(config_dict)
 
-        if config.docker_image:
+        if config.container_image:
             script = (
                 importlib.resources.files("llama_stack")
                 / "distribution/start_container.sh"
             )
-            run_args = [script, config.docker_image]
+            run_args = [script, config.container_image]
         else:
             current_conda_env = os.environ.get("CONDA_DEFAULT_ENV")
             image_name = args.image_name or current_conda_env
diff --git a/llama_stack/distribution/build.py b/llama_stack/distribution/build.py
index b8b4188ac..b8d35ccdc 100644
--- a/llama_stack/distribution/build.py
+++ b/llama_stack/distribution/build.py
@@ -38,7 +38,7 @@ SERVER_DEPENDENCIES = [
 
 
 class ImageType(Enum):
-    docker = "docker"
+    container = "container"
     conda = "conda"
     venv = "venv"
 
@@ -77,8 +77,8 @@ def get_provider_dependencies(
 
             provider_spec = providers_for_api[provider_type]
             deps.extend(provider_spec.pip_packages)
-            if provider_spec.docker_image:
-                raise ValueError("A stack's dependencies cannot have a docker image")
+            if provider_spec.container_image:
+                raise ValueError("A stack's dependencies cannot have a container image")
 
     normal_deps = []
     special_deps = []
@@ -109,23 +109,25 @@ def build_image(
     image_name: str,
     template_name: Optional[str] = None,
 ):
-    docker_image = build_config.distribution_spec.docker_image or "python:3.10-slim"
+    container_image = (
+        build_config.distribution_spec.container_image or "python:3.10-slim"
+    )
 
     normal_deps, special_deps = get_provider_dependencies(
         build_config.distribution_spec.providers
     )
     normal_deps += SERVER_DEPENDENCIES
 
-    if build_config.image_type == ImageType.docker.value:
+    if build_config.image_type == ImageType.container.value:
         script = str(
             importlib.resources.files("llama_stack") / "distribution/build_container.sh"
         )
         args = [
             script,
             image_name,
-            docker_image,
+            container_image,
             str(build_file_path),
-            str(BUILDS_BASE_DIR / ImageType.docker.value),
+            str(BUILDS_BASE_DIR / ImageType.container.value),
             " ".join(normal_deps),
         ]
     elif build_config.image_type == ImageType.conda.value:
diff --git a/llama_stack/distribution/build_container.sh b/llama_stack/distribution/build_container.sh
index 17902de0a..4c2425004 100755
--- a/llama_stack/distribution/build_container.sh
+++ b/llama_stack/distribution/build_container.sh
@@ -13,7 +13,7 @@ PYPI_VERSION=${PYPI_VERSION:-}
 BUILD_PLATFORM=${BUILD_PLATFORM:-}
 
 if [ "$#" -lt 4 ]; then
-  echo "Usage: $0    []" >&2
+  echo "Usage: $0    []" >&2
   echo "Example: $0 my-fastapi-app python:3.9-slim 'fastapi uvicorn' " >&2
   exit 1
 fi
@@ -24,7 +24,7 @@ set -euo pipefail
 
 build_name="$1"
 image_name="distribution-$build_name"
-docker_base=$2
+container_base=$2
 build_file_path=$3
 host_build_dir=$4
 pip_dependencies=$5
@@ -36,14 +36,14 @@ NC='\033[0m' # No Color
 
 SCRIPT_DIR=$(dirname "$(readlink -f "$0")")
 REPO_DIR=$(dirname $(dirname "$SCRIPT_DIR"))
-DOCKER_BINARY=${DOCKER_BINARY:-docker}
-DOCKER_OPTS=${DOCKER_OPTS:-}
+CONTAINER_BINARY=${CONTAINER_BINARY:-docker}
+CONTAINER_OPTS=${CONTAINER_OPTS:-}
 
 TEMP_DIR=$(mktemp -d)
 
-add_to_docker() {
+add_to_container() {
   local input
-  output_file="$TEMP_DIR/Dockerfile"
+  output_file="$TEMP_DIR/Containerfile"
   if [ -t 0 ]; then
     printf '%s\n' "$1" >>"$output_file"
   else
@@ -53,9 +53,9 @@ add_to_docker() {
 }
 
 # Update and install UBI9 components if UBI9 base image is used
-if [[ $docker_base == *"registry.access.redhat.com/ubi9"* ]]; then
-  add_to_docker << EOF
-FROM $docker_base
+if [[ $container_base == *"registry.access.redhat.com/ubi9"* ]]; then
+  add_to_container << EOF
+FROM $container_base
 WORKDIR /app
 
 RUN microdnf -y update && microdnf install -y iputils net-tools wget \
@@ -64,8 +64,8 @@ RUN microdnf -y update && microdnf install -y iputils net-tools wget \
 
 EOF
 else
-  add_to_docker << EOF
-FROM $docker_base
+  add_to_container << EOF
+FROM $container_base
 WORKDIR /app
 
 RUN apt-get update && apt-get install -y \
@@ -82,7 +82,7 @@ fi
 # Add pip dependencies first since llama-stack is what will change most often
 # so we can reuse layers.
 if [ -n "$pip_dependencies" ]; then
-  add_to_docker << EOF
+  add_to_container << EOF
 RUN pip install --no-cache $pip_dependencies
 EOF
 fi
@@ -90,7 +90,7 @@ fi
 if [ -n "$special_pip_deps" ]; then
   IFS='#' read -ra parts <<<"$special_pip_deps"
   for part in "${parts[@]}"; do
-    add_to_docker </dev/null && selinuxenabled; then
   # Disable SELinux labels -- we don't want to relabel the llama-stack source dir
-  DOCKER_OPTS="$DOCKER_OPTS --security-opt label=disable"
+  CONTAINER_OPTS="$CONTAINER_OPTS --security-opt label=disable"
 fi
 
 # Set version tag based on PyPI version
@@ -200,7 +200,7 @@ else
 fi
 
 set -x
-$DOCKER_BINARY build $DOCKER_OPTS $PLATFORM -t $image_tag -f "$TEMP_DIR/Dockerfile" "$REPO_DIR" $mounts
+$CONTAINER_BINARY build $CONTAINER_OPTS $PLATFORM -t $image_tag -f "$TEMP_DIR/Containerfile" "$REPO_DIR" $mounts
 
 # clean up tmp/configs
 set +x
diff --git a/llama_stack/distribution/configure_container.sh b/llama_stack/distribution/configure_container.sh
index 5f64531eb..b01251e46 100755
--- a/llama_stack/distribution/configure_container.sh
+++ b/llama_stack/distribution/configure_container.sh
@@ -6,8 +6,8 @@
 # This source code is licensed under the terms described in the LICENSE file in
 # the root directory of this source tree.
 
-DOCKER_BINARY=${DOCKER_BINARY:-docker}
-DOCKER_OPTS=${DOCKER_OPTS:-}
+CONTAINER_BINARY=${CONTAINER_BINARY:-docker}
+CONTAINER_OPTS=${CONTAINER_OPTS:-}
 LLAMA_STACK_DIR=${LLAMA_STACK_DIR:-}
 
 set -euo pipefail
@@ -24,13 +24,13 @@ if [ $# -lt 2 ]; then
   exit 1
 fi
 
-docker_image="$1"
+container_image="$1"
 host_build_dir="$2"
 container_build_dir="/app/builds"
 
 if command -v selinuxenabled &> /dev/null && selinuxenabled; then
   # Disable SELinux labels
-  DOCKER_OPTS="$DOCKER_OPTS --security-opt label=disable"
+  CONTAINER_OPTS="$CONTAINER_OPTS --security-opt label=disable"
 fi
 
 mounts=""
@@ -39,9 +39,9 @@ if [ -n "$LLAMA_STACK_DIR" ]; then
 fi
 
 set -x
-$DOCKER_BINARY run $DOCKER_OPTS -it \
+$CONTAINER_BINARY run $CONTAINER_OPTS -it \
   --entrypoint "/usr/local/bin/llama" \
   -v $host_build_dir:$container_build_dir \
   $mounts \
-  $docker_image \
+  $container_image \
   stack configure ./llamastack-build.yaml --output-dir $container_build_dir
diff --git a/llama_stack/distribution/datatypes.py b/llama_stack/distribution/datatypes.py
index 0a293cbc2..c1a91cf6c 100644
--- a/llama_stack/distribution/datatypes.py
+++ b/llama_stack/distribution/datatypes.py
@@ -73,7 +73,7 @@ class AutoRoutedProviderSpec(ProviderSpec):
     provider_type: str = "router"
     config_class: str = ""
 
-    docker_image: Optional[str] = None
+    container_image: Optional[str] = None
     routing_table_api: Api
     module: str
     provider_data_validator: Optional[str] = Field(
@@ -89,7 +89,7 @@ class AutoRoutedProviderSpec(ProviderSpec):
 class RoutingTableProviderSpec(ProviderSpec):
     provider_type: str = "routing_table"
     config_class: str = ""
-    docker_image: Optional[str] = None
+    container_image: Optional[str] = None
 
     router_api: Api
     module: str
@@ -101,7 +101,7 @@ class DistributionSpec(BaseModel):
         default="",
         description="Description of the distribution",
     )
-    docker_image: Optional[str] = None
+    container_image: Optional[str] = None
     providers: Dict[str, Union[str, List[str]]] = Field(
         default_factory=dict,
         description="""
@@ -127,9 +127,9 @@ Reference to the distribution this package refers to. For unregistered (adhoc) p
 this could be just a hash
 """,
     )
-    docker_image: Optional[str] = Field(
+    container_image: Optional[str] = Field(
         default=None,
-        description="Reference to the docker image if this package refers to a container",
+        description="Reference to the container image if this package refers to a container",
     )
     apis: List[str] = Field(
         default_factory=list,
@@ -168,5 +168,5 @@ class BuildConfig(BaseModel):
     )
     image_type: str = Field(
         default="conda",
-        description="Type of package to build (conda | docker | venv)",
+        description="Type of package to build (conda | container | venv)",
     )
diff --git a/llama_stack/distribution/start_container.sh b/llama_stack/distribution/start_container.sh
index 3b49a22f8..1a55bf96d 100755
--- a/llama_stack/distribution/start_container.sh
+++ b/llama_stack/distribution/start_container.sh
@@ -6,8 +6,8 @@
 # This source code is licensed under the terms described in the LICENSE file in
 # the root directory of this source tree.
 
-DOCKER_BINARY=${DOCKER_BINARY:-docker}
-DOCKER_OPTS=${DOCKER_OPTS:-}
+CONTAINER_BINARY=${CONTAINER_BINARY:-docker}
+CONTAINER_OPTS=${CONTAINER_OPTS:-}
 LLAMA_CHECKPOINT_DIR=${LLAMA_CHECKPOINT_DIR:-}
 LLAMA_STACK_DIR=${LLAMA_STACK_DIR:-}
 TEST_PYPI_VERSION=${TEST_PYPI_VERSION:-}
@@ -31,7 +31,7 @@ if [ $# -lt 3 ]; then
 fi
 
 build_name="$1"
-docker_image="localhost/distribution-$build_name"
+container_image="localhost/distribution-$build_name"
 shift
 
 yaml_config="$1"
@@ -64,7 +64,7 @@ set -x
 
 if command -v selinuxenabled &> /dev/null && selinuxenabled; then
   # Disable SELinux labels
-  DOCKER_OPTS="$DOCKER_OPTS --security-opt label=disable"
+  CONTAINER_OPTS="$CONTAINER_OPTS --security-opt label=disable"
 fi
 
 mounts=""
@@ -73,7 +73,7 @@ if [ -n "$LLAMA_STACK_DIR" ]; then
 fi
 if [ -n "$LLAMA_CHECKPOINT_DIR" ]; then
   mounts="$mounts -v $LLAMA_CHECKPOINT_DIR:/root/.llama"
-  DOCKER_OPTS="$DOCKER_OPTS --gpus=all"
+  CONTAINER_OPTS="$CONTAINER_OPTS --gpus=all"
 fi
 
 version_tag="latest"
@@ -85,11 +85,11 @@ elif [ -n "$TEST_PYPI_VERSION" ]; then
   version_tag="test-$TEST_PYPI_VERSION"
 fi
 
-$DOCKER_BINARY run $DOCKER_OPTS -it \
+$CONTAINER_BINARY run $CONTAINER_OPTS -it \
   -p $port:$port \
   $env_vars \
   -v "$yaml_config:/app/config.yaml" \
   $mounts \
   --env LLAMA_STACK_PORT=$port \
   --entrypoint='["python", "-m", "llama_stack.distribution.server.server", "--yaml-config", "/app/config.yaml"]' \
-  $docker_image:$version_tag
+  $container_image:$version_tag
diff --git a/llama_stack/providers/datatypes.py b/llama_stack/providers/datatypes.py
index 3e64a62a1..94563879c 100644
--- a/llama_stack/providers/datatypes.py
+++ b/llama_stack/providers/datatypes.py
@@ -150,11 +150,11 @@ class InlineProviderSpec(ProviderSpec):
         default_factory=list,
         description="The pip dependencies needed for this implementation",
     )
-    docker_image: Optional[str] = Field(
+    container_image: Optional[str] = Field(
         default=None,
         description="""
-The docker image to use for this implementation. If one is provided, pip_packages will be ignored.
-If a provider depends on other providers, the dependencies MUST NOT specify a docker image.
+The container image to use for this implementation. If one is provided, pip_packages will be ignored.
+If a provider depends on other providers, the dependencies MUST NOT specify a container image.
 """,
     )
     module: str = Field(
@@ -197,7 +197,7 @@ API responses, specify the adapter here.
     )
 
     @property
-    def docker_image(self) -> Optional[str]:
+    def container_image(self) -> Optional[str]:
         return None
 
     @property
diff --git a/llama_stack/templates/bedrock/bedrock.py b/llama_stack/templates/bedrock/bedrock.py
index c80625cf6..da792e461 100644
--- a/llama_stack/templates/bedrock/bedrock.py
+++ b/llama_stack/templates/bedrock/bedrock.py
@@ -70,7 +70,7 @@ def get_distribution_template() -> DistributionTemplate:
         name=name,
         distro_type="self_hosted",
         description="Use AWS Bedrock for running LLM inference and safety",
-        docker_image=None,
+        container_image=None,
         template_path=Path(__file__).parent / "doc_template.md",
         providers=providers,
         default_models=default_models,
diff --git a/llama_stack/templates/cerebras/cerebras.py b/llama_stack/templates/cerebras/cerebras.py
index df3b55ddd..8f6bd77af 100644
--- a/llama_stack/templates/cerebras/cerebras.py
+++ b/llama_stack/templates/cerebras/cerebras.py
@@ -92,7 +92,7 @@ def get_distribution_template() -> DistributionTemplate:
         name="cerebras",
         distro_type="self_hosted",
         description="Use Cerebras for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=Path(__file__).parent / "doc_template.md",
         providers=providers,
         default_models=default_models,
diff --git a/llama_stack/templates/experimental-post-training/build.yaml b/llama_stack/templates/experimental-post-training/build.yaml
index 4997ab8a3..618e8ff97 100644
--- a/llama_stack/templates/experimental-post-training/build.yaml
+++ b/llama_stack/templates/experimental-post-training/build.yaml
@@ -2,7 +2,7 @@ version: '2'
 name: experimental-post-training
 distribution_spec:
   description: Experimental template for post training
-  docker_image: null
+  container_image: null
   providers:
     inference:
     - inline::meta-reference
diff --git a/llama_stack/templates/experimental-post-training/run.yaml b/llama_stack/templates/experimental-post-training/run.yaml
index 2e0ee029b..87465137f 100644
--- a/llama_stack/templates/experimental-post-training/run.yaml
+++ b/llama_stack/templates/experimental-post-training/run.yaml
@@ -1,6 +1,6 @@
 version: '2'
 image_name: experimental-post-training
-docker_image: null
+container_image: null
 conda_env: experimental-post-training
 apis:
 - agents
diff --git a/llama_stack/templates/fireworks/fireworks.py b/llama_stack/templates/fireworks/fireworks.py
index 8add75f7d..c94074a70 100644
--- a/llama_stack/templates/fireworks/fireworks.py
+++ b/llama_stack/templates/fireworks/fireworks.py
@@ -98,7 +98,7 @@ def get_distribution_template() -> DistributionTemplate:
         name=name,
         distro_type="self_hosted",
         description="Use Fireworks.AI for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=Path(__file__).parent / "doc_template.md",
         providers=providers,
         default_models=default_models,
diff --git a/llama_stack/templates/hf-endpoint/hf_endpoint.py b/llama_stack/templates/hf-endpoint/hf_endpoint.py
index 54aaa56ac..04e2a53b5 100644
--- a/llama_stack/templates/hf-endpoint/hf_endpoint.py
+++ b/llama_stack/templates/hf-endpoint/hf_endpoint.py
@@ -88,7 +88,7 @@ def get_distribution_template() -> DistributionTemplate:
         name=name,
         distro_type="self_hosted",
         description="Use (an external) Hugging Face Inference Endpoint for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=None,
         providers=providers,
         default_models=[inference_model, safety_model],
diff --git a/llama_stack/templates/hf-serverless/hf_serverless.py b/llama_stack/templates/hf-serverless/hf_serverless.py
index 788faa986..af8d77629 100644
--- a/llama_stack/templates/hf-serverless/hf_serverless.py
+++ b/llama_stack/templates/hf-serverless/hf_serverless.py
@@ -89,7 +89,7 @@ def get_distribution_template() -> DistributionTemplate:
         name=name,
         distro_type="self_hosted",
         description="Use (an external) Hugging Face Inference Endpoint for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=None,
         providers=providers,
         default_models=[inference_model, safety_model],
diff --git a/llama_stack/templates/nvidia/nvidia.py b/llama_stack/templates/nvidia/nvidia.py
index cfa86dbe7..d5518ecc9 100644
--- a/llama_stack/templates/nvidia/nvidia.py
+++ b/llama_stack/templates/nvidia/nvidia.py
@@ -68,7 +68,7 @@ def get_distribution_template() -> DistributionTemplate:
         name="nvidia",
         distro_type="remote_hosted",
         description="Use NVIDIA NIM for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=Path(__file__).parent / "doc_template.md",
         providers=providers,
         default_models=default_models,
diff --git a/llama_stack/templates/ollama/ollama.py b/llama_stack/templates/ollama/ollama.py
index 0473f8692..2288ea3a6 100644
--- a/llama_stack/templates/ollama/ollama.py
+++ b/llama_stack/templates/ollama/ollama.py
@@ -90,7 +90,7 @@ def get_distribution_template() -> DistributionTemplate:
         name=name,
         distro_type="self_hosted",
         description="Use (an external) Ollama server for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=Path(__file__).parent / "doc_template.md",
         providers=providers,
         default_models=[inference_model, safety_model],
diff --git a/llama_stack/templates/template.py b/llama_stack/templates/template.py
index 5bb88c821..d9696b23d 100644
--- a/llama_stack/templates/template.py
+++ b/llama_stack/templates/template.py
@@ -37,7 +37,7 @@ class RunConfigSettings(BaseModel):
         self,
         name: str,
         providers: Dict[str, List[str]],
-        docker_image: Optional[str] = None,
+        container_image: Optional[str] = None,
     ) -> StackRunConfig:
         provider_registry = get_provider_registry()
 
@@ -83,7 +83,7 @@ class RunConfigSettings(BaseModel):
 
         return StackRunConfig(
             image_name=name,
-            docker_image=docker_image,
+            container_image=container_image,
             conda_env=name,
             apis=apis,
             providers=provider_configs,
@@ -113,7 +113,7 @@ class DistributionTemplate(BaseModel):
 
     # Optional configuration
     run_config_env_vars: Optional[Dict[str, Tuple[str, str]]] = None
-    docker_image: Optional[str] = None
+    container_image: Optional[str] = None
 
     default_models: Optional[List[ModelInput]] = None
 
@@ -122,7 +122,7 @@ class DistributionTemplate(BaseModel):
             name=self.name,
             distribution_spec=DistributionSpec(
                 description=self.description,
-                docker_image=self.docker_image,
+                container_image=self.container_image,
                 providers=self.providers,
             ),
             image_type="conda",  # default to conda, can be overridden
@@ -170,7 +170,7 @@ class DistributionTemplate(BaseModel):
 
         for yaml_pth, settings in self.run_configs.items():
             run_config = settings.run_config(
-                self.name, self.providers, self.docker_image
+                self.name, self.providers, self.container_image
             )
             with open(yaml_output_dir / yaml_pth, "w") as f:
                 yaml.safe_dump(
diff --git a/llama_stack/templates/tgi/tgi.py b/llama_stack/templates/tgi/tgi.py
index b62e7719e..02187f986 100644
--- a/llama_stack/templates/tgi/tgi.py
+++ b/llama_stack/templates/tgi/tgi.py
@@ -92,7 +92,7 @@ def get_distribution_template() -> DistributionTemplate:
         name=name,
         distro_type="self_hosted",
         description="Use (an external) TGI server for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=Path(__file__).parent / "doc_template.md",
         providers=providers,
         default_models=[inference_model, safety_model],
diff --git a/llama_stack/templates/together/together.py b/llama_stack/templates/together/together.py
index b51918a6c..28c01095a 100644
--- a/llama_stack/templates/together/together.py
+++ b/llama_stack/templates/together/together.py
@@ -96,7 +96,7 @@ def get_distribution_template() -> DistributionTemplate:
         name=name,
         distro_type="self_hosted",
         description="Use Together.AI for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=Path(__file__).parent / "doc_template.md",
         providers=providers,
         default_models=default_models,
diff --git a/llama_stack/templates/vllm-gpu/vllm.py b/llama_stack/templates/vllm-gpu/vllm.py
index dd80c15dc..1f3cf4b35 100644
--- a/llama_stack/templates/vllm-gpu/vllm.py
+++ b/llama_stack/templates/vllm-gpu/vllm.py
@@ -84,7 +84,7 @@ def get_distribution_template() -> DistributionTemplate:
         name=name,
         distro_type="self_hosted",
         description="Use a built-in vLLM engine for running LLM inference",
-        docker_image=None,
+        container_image=None,
         template_path=None,
         providers=providers,
         default_models=[inference_model],

From 3d4c53dfec7715fc8035d4d5b8929619ab12da25 Mon Sep 17 00:00:00 2001
From: Dinesh Yeduguru 
Date: Fri, 17 Jan 2025 16:40:58 -0800
Subject: [PATCH 057/107] add mcp runtime as default to all providers (#816)

# What does this PR do?

This is needed to have the notebook work with MCP
---
 distributions/dependencies.json               | 349 +++++++++---------
 .../remote_hosted_distro/nvidia.md            |   2 +-
 .../self_hosted_distro/bedrock.md             |   2 +-
 .../self_hosted_distro/fireworks.md           |   2 +-
 .../self_hosted_distro/meta-reference-gpu.md  |   2 +-
 .../meta-reference-quantized-gpu.md           |   2 +-
 .../self_hosted_distro/remote-vllm.md         |   2 +-
 .../distributions/self_hosted_distro/tgi.md   |   2 +-
 .../self_hosted_distro/together.md            |   2 +-
 llama_stack/templates/bedrock/bedrock.py      |   1 +
 llama_stack/templates/bedrock/build.yaml      |   1 +
 llama_stack/templates/bedrock/run.yaml        |   3 +
 llama_stack/templates/fireworks/build.yaml    |   1 +
 llama_stack/templates/fireworks/fireworks.py  |   1 +
 .../templates/fireworks/run-with-safety.yaml  |   3 +
 llama_stack/templates/fireworks/run.yaml      |   3 +
 llama_stack/templates/hf-endpoint/build.yaml  |   1 +
 .../templates/hf-endpoint/hf_endpoint.py      |   1 +
 .../hf-endpoint/run-with-safety.yaml          |   3 +
 llama_stack/templates/hf-endpoint/run.yaml    |   3 +
 .../templates/hf-serverless/build.yaml        |   1 +
 .../templates/hf-serverless/hf_serverless.py  |   1 +
 .../hf-serverless/run-with-safety.yaml        |   3 +
 llama_stack/templates/hf-serverless/run.yaml  |   3 +
 .../templates/meta-reference-gpu/build.yaml   |   1 +
 .../meta-reference-gpu/meta_reference.py      |   1 +
 .../meta-reference-gpu/run-with-safety.yaml   |   3 +
 .../templates/meta-reference-gpu/run.yaml     |   3 +
 .../meta-reference-quantized-gpu/build.yaml   |   1 +
 .../meta_reference.py                         |   1 +
 .../meta-reference-quantized-gpu/run.yaml     |   3 +
 llama_stack/templates/nvidia/build.yaml       |   1 +
 llama_stack/templates/nvidia/nvidia.py        |   1 +
 llama_stack/templates/nvidia/run.yaml         |   3 +
 llama_stack/templates/remote-vllm/build.yaml  |   1 +
 .../remote-vllm/run-with-safety.yaml          |   3 +
 llama_stack/templates/remote-vllm/run.yaml    |   3 +
 llama_stack/templates/remote-vllm/vllm.py     |   1 +
 llama_stack/templates/tgi/build.yaml          |   1 +
 .../templates/tgi/run-with-safety.yaml        |   3 +
 llama_stack/templates/tgi/run.yaml            |   3 +
 llama_stack/templates/tgi/tgi.py              |   1 +
 llama_stack/templates/together/build.yaml     |   1 +
 .../templates/together/run-with-safety.yaml   |   3 +
 llama_stack/templates/together/run.yaml       |   3 +
 llama_stack/templates/together/together.py    |   1 +
 llama_stack/templates/vllm-gpu/build.yaml     |   1 +
 llama_stack/templates/vllm-gpu/run.yaml       |   3 +
 llama_stack/templates/vllm-gpu/vllm.py        |   1 +
 49 files changed, 264 insertions(+), 177 deletions(-)

diff --git a/distributions/dependencies.json b/distributions/dependencies.json
index c3d643695..7b5d8b002 100644
--- a/distributions/dependencies.json
+++ b/distributions/dependencies.json
@@ -1,9 +1,43 @@
 {
-  "bedrock": [
+  "hf-serverless": [
+    "aiohttp",
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "huggingface_hub",
+    "matplotlib",
+    "mcp",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
+  "together": [
     "aiosqlite",
     "autoevals",
     "blobfile",
-    "boto3",
     "chardet",
     "chromadb-client",
     "datasets",
@@ -12,6 +46,75 @@
     "fire",
     "httpx",
     "matplotlib",
+    "mcp",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "together",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
+  "vllm-gpu": [
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "matplotlib",
+    "mcp",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "vllm",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
+  "remote-vllm": [
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "matplotlib",
+    "mcp",
     "nltk",
     "numpy",
     "openai",
@@ -45,6 +148,7 @@
     "fireworks-ai",
     "httpx",
     "matplotlib",
+    "mcp",
     "nltk",
     "numpy",
     "openai",
@@ -65,7 +169,7 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
-  "hf-endpoint": [
+  "tgi": [
     "aiohttp",
     "aiosqlite",
     "autoevals",
@@ -79,6 +183,7 @@
     "httpx",
     "huggingface_hub",
     "matplotlib",
+    "mcp",
     "nltk",
     "numpy",
     "openai",
@@ -99,11 +204,11 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
-  "hf-serverless": [
-    "aiohttp",
+  "bedrock": [
     "aiosqlite",
     "autoevals",
     "blobfile",
+    "boto3",
     "chardet",
     "chromadb-client",
     "datasets",
@@ -111,8 +216,8 @@
     "fastapi",
     "fire",
     "httpx",
-    "huggingface_hub",
     "matplotlib",
+    "mcp",
     "nltk",
     "numpy",
     "openai",
@@ -148,6 +253,7 @@
     "httpx",
     "lm-format-enforcer",
     "matplotlib",
+    "mcp",
     "nltk",
     "numpy",
     "openai",
@@ -172,6 +278,38 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
+  "nvidia": [
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "chardet",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "matplotlib",
+    "mcp",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
   "meta-reference-quantized-gpu": [
     "accelerate",
     "aiosqlite",
@@ -188,6 +326,7 @@
     "httpx",
     "lm-format-enforcer",
     "matplotlib",
+    "mcp",
     "nltk",
     "numpy",
     "openai",
@@ -213,6 +352,39 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
+  "cerebras": [
+    "aiosqlite",
+    "autoevals",
+    "blobfile",
+    "cerebras_cloud_sdk",
+    "chardet",
+    "chromadb-client",
+    "datasets",
+    "faiss-cpu",
+    "fastapi",
+    "fire",
+    "httpx",
+    "matplotlib",
+    "nltk",
+    "numpy",
+    "openai",
+    "opentelemetry-exporter-otlp-proto-http",
+    "opentelemetry-sdk",
+    "pandas",
+    "pillow",
+    "psycopg2-binary",
+    "pypdf",
+    "redis",
+    "requests",
+    "scikit-learn",
+    "scipy",
+    "sentencepiece",
+    "tqdm",
+    "transformers",
+    "uvicorn",
+    "sentence-transformers --no-deps",
+    "torch --index-url https://download.pytorch.org/whl/cpu"
+  ],
   "ollama": [
     "aiohttp",
     "aiosqlite",
@@ -247,7 +419,7 @@
     "sentence-transformers --no-deps",
     "torch --index-url https://download.pytorch.org/whl/cpu"
   ],
-  "tgi": [
+  "hf-endpoint": [
     "aiohttp",
     "aiosqlite",
     "autoevals",
@@ -261,168 +433,7 @@
     "httpx",
     "huggingface_hub",
     "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "together": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "together",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "remote-vllm": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "cerebras": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "cerebras_cloud_sdk",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "vllm-gpu": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "chromadb-client",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
-    "nltk",
-    "numpy",
-    "openai",
-    "opentelemetry-exporter-otlp-proto-http",
-    "opentelemetry-sdk",
-    "pandas",
-    "pillow",
-    "psycopg2-binary",
-    "pypdf",
-    "redis",
-    "requests",
-    "scikit-learn",
-    "scipy",
-    "sentencepiece",
-    "tqdm",
-    "transformers",
-    "uvicorn",
-    "vllm",
-    "sentence-transformers --no-deps",
-    "torch --index-url https://download.pytorch.org/whl/cpu"
-  ],
-  "nvidia": [
-    "aiosqlite",
-    "autoevals",
-    "blobfile",
-    "chardet",
-    "datasets",
-    "faiss-cpu",
-    "fastapi",
-    "fire",
-    "httpx",
-    "matplotlib",
+    "mcp",
     "nltk",
     "numpy",
     "openai",
diff --git a/docs/source/distributions/remote_hosted_distro/nvidia.md b/docs/source/distributions/remote_hosted_distro/nvidia.md
index 7e3446863..4028ed384 100644
--- a/docs/source/distributions/remote_hosted_distro/nvidia.md
+++ b/docs/source/distributions/remote_hosted_distro/nvidia.md
@@ -12,7 +12,7 @@ The `llamastack/distribution-nvidia` distribution consists of the following prov
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
 
 
 ### Environment Variables
diff --git a/docs/source/distributions/self_hosted_distro/bedrock.md b/docs/source/distributions/self_hosted_distro/bedrock.md
index 71adfad09..dd4e51264 100644
--- a/docs/source/distributions/self_hosted_distro/bedrock.md
+++ b/docs/source/distributions/self_hosted_distro/bedrock.md
@@ -19,7 +19,7 @@ The `llamastack/distribution-bedrock` distribution consists of the following pro
 | safety | `remote::bedrock` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
 
 
 
diff --git a/docs/source/distributions/self_hosted_distro/fireworks.md b/docs/source/distributions/self_hosted_distro/fireworks.md
index 335309729..7ed174984 100644
--- a/docs/source/distributions/self_hosted_distro/fireworks.md
+++ b/docs/source/distributions/self_hosted_distro/fireworks.md
@@ -22,7 +22,7 @@ The `llamastack/distribution-fireworks` distribution consists of the following p
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
 
 
 ### Environment Variables
diff --git a/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md b/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md
index a89719dea..269354e98 100644
--- a/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md
+++ b/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md
@@ -22,7 +22,7 @@ The `llamastack/distribution-meta-reference-gpu` distribution consists of the fo
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
 
 
 Note that you need access to nvidia GPUs to run this distribution. This distribution is not compatible with CPU-only machines or machines with AMD GPUs.
diff --git a/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md b/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md
index 26ed5d05b..937dbbdbd 100644
--- a/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md
+++ b/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md
@@ -22,7 +22,7 @@ The `llamastack/distribution-meta-reference-quantized-gpu` distribution consists
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
 
 
 The only difference vs. the `meta-reference-gpu` distribution is that it has support for more efficient inference -- with fp8, int4 quantization, etc.
diff --git a/docs/source/distributions/self_hosted_distro/remote-vllm.md b/docs/source/distributions/self_hosted_distro/remote-vllm.md
index 5b29c402f..2bb5329b9 100644
--- a/docs/source/distributions/self_hosted_distro/remote-vllm.md
+++ b/docs/source/distributions/self_hosted_distro/remote-vllm.md
@@ -21,7 +21,7 @@ The `llamastack/distribution-remote-vllm` distribution consists of the following
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
 
 
 You can use this distribution if you have GPUs and want to run an independent vLLM server container for running inference.
diff --git a/docs/source/distributions/self_hosted_distro/tgi.md b/docs/source/distributions/self_hosted_distro/tgi.md
index f4f705b12..0fd6a693c 100644
--- a/docs/source/distributions/self_hosted_distro/tgi.md
+++ b/docs/source/distributions/self_hosted_distro/tgi.md
@@ -23,7 +23,7 @@ The `llamastack/distribution-tgi` distribution consists of the following provide
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
 
 
 You can use this distribution if you have GPUs and want to run an independent TGI server container for running inference.
diff --git a/docs/source/distributions/self_hosted_distro/together.md b/docs/source/distributions/self_hosted_distro/together.md
index 3b476c9bf..e990e273f 100644
--- a/docs/source/distributions/self_hosted_distro/together.md
+++ b/docs/source/distributions/self_hosted_distro/together.md
@@ -22,7 +22,7 @@ The `llamastack/distribution-together` distribution consists of the following pr
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
 
 
 ### Environment Variables
diff --git a/llama_stack/templates/bedrock/bedrock.py b/llama_stack/templates/bedrock/bedrock.py
index da792e461..668134be8 100644
--- a/llama_stack/templates/bedrock/bedrock.py
+++ b/llama_stack/templates/bedrock/bedrock.py
@@ -30,6 +30,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
     name = "bedrock"
diff --git a/llama_stack/templates/bedrock/build.yaml b/llama_stack/templates/bedrock/build.yaml
index 794e54306..95b8684e3 100644
--- a/llama_stack/templates/bedrock/build.yaml
+++ b/llama_stack/templates/bedrock/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/bedrock/run.yaml b/llama_stack/templates/bedrock/run.yaml
index 3a6922ae7..118723bbc 100644
--- a/llama_stack/templates/bedrock/run.yaml
+++ b/llama_stack/templates/bedrock/run.yaml
@@ -81,6 +81,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/bedrock}/registry.db
diff --git a/llama_stack/templates/fireworks/build.yaml b/llama_stack/templates/fireworks/build.yaml
index 504c913bd..d8e1e27ee 100644
--- a/llama_stack/templates/fireworks/build.yaml
+++ b/llama_stack/templates/fireworks/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/fireworks/fireworks.py b/llama_stack/templates/fireworks/fireworks.py
index c94074a70..14fd392c4 100644
--- a/llama_stack/templates/fireworks/fireworks.py
+++ b/llama_stack/templates/fireworks/fireworks.py
@@ -39,6 +39,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
 
diff --git a/llama_stack/templates/fireworks/run-with-safety.yaml b/llama_stack/templates/fireworks/run-with-safety.yaml
index 8fefbd98a..dd21120ed 100644
--- a/llama_stack/templates/fireworks/run-with-safety.yaml
+++ b/llama_stack/templates/fireworks/run-with-safety.yaml
@@ -92,6 +92,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/fireworks}/registry.db
diff --git a/llama_stack/templates/fireworks/run.yaml b/llama_stack/templates/fireworks/run.yaml
index 53128f456..993417b50 100644
--- a/llama_stack/templates/fireworks/run.yaml
+++ b/llama_stack/templates/fireworks/run.yaml
@@ -86,6 +86,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/fireworks}/registry.db
diff --git a/llama_stack/templates/hf-endpoint/build.yaml b/llama_stack/templates/hf-endpoint/build.yaml
index 43486030e..f4fdc4a3d 100644
--- a/llama_stack/templates/hf-endpoint/build.yaml
+++ b/llama_stack/templates/hf-endpoint/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/hf-endpoint/hf_endpoint.py b/llama_stack/templates/hf-endpoint/hf_endpoint.py
index 04e2a53b5..1a5c23a42 100644
--- a/llama_stack/templates/hf-endpoint/hf_endpoint.py
+++ b/llama_stack/templates/hf-endpoint/hf_endpoint.py
@@ -34,6 +34,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
     name = "hf-endpoint"
diff --git a/llama_stack/templates/hf-endpoint/run-with-safety.yaml b/llama_stack/templates/hf-endpoint/run-with-safety.yaml
index 6a52ca861..537e4024f 100644
--- a/llama_stack/templates/hf-endpoint/run-with-safety.yaml
+++ b/llama_stack/templates/hf-endpoint/run-with-safety.yaml
@@ -91,6 +91,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/hf-endpoint}/registry.db
diff --git a/llama_stack/templates/hf-endpoint/run.yaml b/llama_stack/templates/hf-endpoint/run.yaml
index c019c587a..b31f28434 100644
--- a/llama_stack/templates/hf-endpoint/run.yaml
+++ b/llama_stack/templates/hf-endpoint/run.yaml
@@ -86,6 +86,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/hf-endpoint}/registry.db
diff --git a/llama_stack/templates/hf-serverless/build.yaml b/llama_stack/templates/hf-serverless/build.yaml
index e1328bd58..d075a7449 100644
--- a/llama_stack/templates/hf-serverless/build.yaml
+++ b/llama_stack/templates/hf-serverless/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/hf-serverless/hf_serverless.py b/llama_stack/templates/hf-serverless/hf_serverless.py
index af8d77629..0292f13e2 100644
--- a/llama_stack/templates/hf-serverless/hf_serverless.py
+++ b/llama_stack/templates/hf-serverless/hf_serverless.py
@@ -34,6 +34,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
 
diff --git a/llama_stack/templates/hf-serverless/run-with-safety.yaml b/llama_stack/templates/hf-serverless/run-with-safety.yaml
index 0a64de358..484b2d0bd 100644
--- a/llama_stack/templates/hf-serverless/run-with-safety.yaml
+++ b/llama_stack/templates/hf-serverless/run-with-safety.yaml
@@ -91,6 +91,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/hf-serverless}/registry.db
diff --git a/llama_stack/templates/hf-serverless/run.yaml b/llama_stack/templates/hf-serverless/run.yaml
index f04213533..a75baf1f9 100644
--- a/llama_stack/templates/hf-serverless/run.yaml
+++ b/llama_stack/templates/hf-serverless/run.yaml
@@ -86,6 +86,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/hf-serverless}/registry.db
diff --git a/llama_stack/templates/meta-reference-gpu/build.yaml b/llama_stack/templates/meta-reference-gpu/build.yaml
index 9ad7b26bf..a75d3604b 100644
--- a/llama_stack/templates/meta-reference-gpu/build.yaml
+++ b/llama_stack/templates/meta-reference-gpu/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/meta-reference-gpu/meta_reference.py b/llama_stack/templates/meta-reference-gpu/meta_reference.py
index 7364ee422..584d38256 100644
--- a/llama_stack/templates/meta-reference-gpu/meta_reference.py
+++ b/llama_stack/templates/meta-reference-gpu/meta_reference.py
@@ -38,6 +38,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
     name = "meta-reference-gpu"
diff --git a/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml b/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
index 591afa2be..9dbdb6fa5 100644
--- a/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
+++ b/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
@@ -93,6 +93,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/meta-reference-gpu}/registry.db
diff --git a/llama_stack/templates/meta-reference-gpu/run.yaml b/llama_stack/templates/meta-reference-gpu/run.yaml
index ecde69fdf..6465215f0 100644
--- a/llama_stack/templates/meta-reference-gpu/run.yaml
+++ b/llama_stack/templates/meta-reference-gpu/run.yaml
@@ -87,6 +87,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/meta-reference-gpu}/registry.db
diff --git a/llama_stack/templates/meta-reference-quantized-gpu/build.yaml b/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
index e6b64ea1e..4c3e2f492 100644
--- a/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
+++ b/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py b/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py
index 5c40134af..56293f42c 100644
--- a/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py
+++ b/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py
@@ -33,6 +33,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
     default_tool_groups = [
diff --git a/llama_stack/templates/meta-reference-quantized-gpu/run.yaml b/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
index ff0affafb..059034741 100644
--- a/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
+++ b/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
@@ -89,6 +89,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/meta-reference-quantized-gpu}/registry.db
diff --git a/llama_stack/templates/nvidia/build.yaml b/llama_stack/templates/nvidia/build.yaml
index 56124552b..7bd2a3865 100644
--- a/llama_stack/templates/nvidia/build.yaml
+++ b/llama_stack/templates/nvidia/build.yaml
@@ -26,4 +26,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/nvidia/nvidia.py b/llama_stack/templates/nvidia/nvidia.py
index d5518ecc9..e72fe359f 100644
--- a/llama_stack/templates/nvidia/nvidia.py
+++ b/llama_stack/templates/nvidia/nvidia.py
@@ -29,6 +29,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
 
diff --git a/llama_stack/templates/nvidia/run.yaml b/llama_stack/templates/nvidia/run.yaml
index 578f70c9d..07c901371 100644
--- a/llama_stack/templates/nvidia/run.yaml
+++ b/llama_stack/templates/nvidia/run.yaml
@@ -83,6 +83,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/nvidia}/registry.db
diff --git a/llama_stack/templates/remote-vllm/build.yaml b/llama_stack/templates/remote-vllm/build.yaml
index 7398ab96d..6f301914c 100644
--- a/llama_stack/templates/remote-vllm/build.yaml
+++ b/llama_stack/templates/remote-vllm/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/remote-vllm/run-with-safety.yaml b/llama_stack/templates/remote-vllm/run-with-safety.yaml
index 9c030e8b2..5e5bd6af6 100644
--- a/llama_stack/templates/remote-vllm/run-with-safety.yaml
+++ b/llama_stack/templates/remote-vllm/run-with-safety.yaml
@@ -93,6 +93,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/remote-vllm}/registry.db
diff --git a/llama_stack/templates/remote-vllm/run.yaml b/llama_stack/templates/remote-vllm/run.yaml
index 053b254bd..4eac4dad7 100644
--- a/llama_stack/templates/remote-vllm/run.yaml
+++ b/llama_stack/templates/remote-vllm/run.yaml
@@ -87,6 +87,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/remote-vllm}/registry.db
diff --git a/llama_stack/templates/remote-vllm/vllm.py b/llama_stack/templates/remote-vllm/vllm.py
index 229d7f172..296e2b4f5 100644
--- a/llama_stack/templates/remote-vllm/vllm.py
+++ b/llama_stack/templates/remote-vllm/vllm.py
@@ -36,6 +36,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
     name = "remote-vllm"
diff --git a/llama_stack/templates/tgi/build.yaml b/llama_stack/templates/tgi/build.yaml
index 3bcacffb0..4391ddd5d 100644
--- a/llama_stack/templates/tgi/build.yaml
+++ b/llama_stack/templates/tgi/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/tgi/run-with-safety.yaml b/llama_stack/templates/tgi/run-with-safety.yaml
index 070daedc1..9bd06d650 100644
--- a/llama_stack/templates/tgi/run-with-safety.yaml
+++ b/llama_stack/templates/tgi/run-with-safety.yaml
@@ -86,6 +86,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/tgi}/registry.db
diff --git a/llama_stack/templates/tgi/run.yaml b/llama_stack/templates/tgi/run.yaml
index e9696c584..2fc1b52d9 100644
--- a/llama_stack/templates/tgi/run.yaml
+++ b/llama_stack/templates/tgi/run.yaml
@@ -85,6 +85,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/tgi}/registry.db
diff --git a/llama_stack/templates/tgi/tgi.py b/llama_stack/templates/tgi/tgi.py
index 02187f986..8ad9725e3 100644
--- a/llama_stack/templates/tgi/tgi.py
+++ b/llama_stack/templates/tgi/tgi.py
@@ -36,6 +36,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
     name = "tgi"
diff --git a/llama_stack/templates/together/build.yaml b/llama_stack/templates/together/build.yaml
index ad970f405..ea7387a24 100644
--- a/llama_stack/templates/together/build.yaml
+++ b/llama_stack/templates/together/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/together/run-with-safety.yaml b/llama_stack/templates/together/run-with-safety.yaml
index 4e162aab3..c1461d75d 100644
--- a/llama_stack/templates/together/run-with-safety.yaml
+++ b/llama_stack/templates/together/run-with-safety.yaml
@@ -92,6 +92,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/together}/registry.db
diff --git a/llama_stack/templates/together/run.yaml b/llama_stack/templates/together/run.yaml
index 3c4844447..da25fd144 100644
--- a/llama_stack/templates/together/run.yaml
+++ b/llama_stack/templates/together/run.yaml
@@ -86,6 +86,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/together}/registry.db
diff --git a/llama_stack/templates/together/together.py b/llama_stack/templates/together/together.py
index 28c01095a..1e2def3bd 100644
--- a/llama_stack/templates/together/together.py
+++ b/llama_stack/templates/together/together.py
@@ -39,6 +39,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
     name = "together"
diff --git a/llama_stack/templates/vllm-gpu/build.yaml b/llama_stack/templates/vllm-gpu/build.yaml
index e068fa97e..e8a1693d0 100644
--- a/llama_stack/templates/vllm-gpu/build.yaml
+++ b/llama_stack/templates/vllm-gpu/build.yaml
@@ -28,4 +28,5 @@ distribution_spec:
     - remote::tavily-search
     - inline::code-interpreter
     - inline::memory-runtime
+    - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/vllm-gpu/run.yaml b/llama_stack/templates/vllm-gpu/run.yaml
index 1cb44b052..cc0ff047f 100644
--- a/llama_stack/templates/vllm-gpu/run.yaml
+++ b/llama_stack/templates/vllm-gpu/run.yaml
@@ -89,6 +89,9 @@ providers:
   - provider_id: memory-runtime
     provider_type: inline::memory-runtime
     config: {}
+  - provider_id: model-context-protocol
+    provider_type: remote::model-context-protocol
+    config: {}
 metadata_store:
   type: sqlite
   db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/vllm-gpu}/registry.db
diff --git a/llama_stack/templates/vllm-gpu/vllm.py b/llama_stack/templates/vllm-gpu/vllm.py
index 1f3cf4b35..71b24482d 100644
--- a/llama_stack/templates/vllm-gpu/vllm.py
+++ b/llama_stack/templates/vllm-gpu/vllm.py
@@ -33,6 +33,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::tavily-search",
             "inline::code-interpreter",
             "inline::memory-runtime",
+            "remote::model-context-protocol",
         ],
     }
 

From 3e7496e835af2d7a9f5afea750925ac2e1ebf11f Mon Sep 17 00:00:00 2001
From: Xi Yan 
Date: Fri, 17 Jan 2025 17:07:28 -0800
Subject: [PATCH 058/107] fix vllm base64 image inference (#815)

# What does this PR do?

- fix base64 based image url for vllm
- add a test case for base64 based image_url
- fixes issue: https://github.com/meta-llama/llama-stack/issues/571

## Test Plan

```
LLAMA_STACK_BASE_URL=http://localhost:8321 pytest -v ./tests/client-sdk/inference/test_inference.py::test_image_chat_completion_base64_url
```

image

## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 .../providers/remote/inference/vllm/vllm.py   |   4 +-
 tests/client-sdk/inference/dog.png            | Bin 0 -> 425075 bytes
 tests/client-sdk/inference/test_inference.py  |  41 ++++++++++++++++++
 3 files changed, 42 insertions(+), 3 deletions(-)
 create mode 100644 tests/client-sdk/inference/dog.png

diff --git a/llama_stack/providers/remote/inference/vllm/vllm.py b/llama_stack/providers/remote/inference/vllm/vllm.py
index 317d05207..81c746cce 100644
--- a/llama_stack/providers/remote/inference/vllm/vllm.py
+++ b/llama_stack/providers/remote/inference/vllm/vllm.py
@@ -176,10 +176,8 @@ class VLLMInferenceAdapter(Inference, ModelsProtocolPrivate):
         media_present = request_has_media(request)
         if isinstance(request, ChatCompletionRequest):
             if media_present:
-                # vllm does not seem to work well with image urls, so we download the images
                 input_dict["messages"] = [
-                    await convert_message_to_openai_dict(m, download=True)
-                    for m in request.messages
+                    await convert_message_to_openai_dict(m) for m in request.messages
                 ]
             else:
                 input_dict["prompt"] = await chat_completion_request_to_prompt(
diff --git a/tests/client-sdk/inference/dog.png b/tests/client-sdk/inference/dog.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d502e6064708230e9413e654100fd9c8a47b051
GIT binary patch
literal 425075
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μdo-An5ET6%M54+3pZ~#`kQ*2Qcs_
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�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

6lI<*|64a0TEOtd+jj$!eZaL65ti;M&~e)8tb87 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 0 assert any(expected in streamed_content for expected in {"dog", "puppy", "pup"}) + + +def test_image_chat_completion_base64_url( + llama_stack_client, vision_model_id, base64_image_url +): + + message = { + "role": "user", + "content": [ + { + "type": "image", + "url": { + "uri": base64_image_url, + }, + }, + { + "type": "text", + "text": "Describe what is in this image.", + }, + ], + } + response = llama_stack_client.inference.chat_completion( + model_id=vision_model_id, + messages=[message], + stream=False, + ) + message_content = response.completion_message.content.lower().strip() + assert len(message_content) > 0 From 3a9468ce9b06dc066d20622c3e5e916163ba2218 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Fri, 17 Jan 2025 18:33:40 -0800 Subject: [PATCH 059/107] fix again vllm for non base64 (#818) # What does this PR do? - previous fix introduced regression for non base64 image - add back download, and base64 check ## Test Plan image ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- llama_stack/providers/remote/inference/vllm/vllm.py | 3 ++- llama_stack/providers/utils/inference/prompt_adapter.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/llama_stack/providers/remote/inference/vllm/vllm.py b/llama_stack/providers/remote/inference/vllm/vllm.py index 81c746cce..1dbb4ecfa 100644 --- a/llama_stack/providers/remote/inference/vllm/vllm.py +++ b/llama_stack/providers/remote/inference/vllm/vllm.py @@ -177,7 +177,8 @@ class VLLMInferenceAdapter(Inference, ModelsProtocolPrivate): if isinstance(request, ChatCompletionRequest): if media_present: input_dict["messages"] = [ - await convert_message_to_openai_dict(m) for m in request.messages + await convert_message_to_openai_dict(m, download=True) + for m in request.messages ] else: input_dict["prompt"] = await chat_completion_request_to_prompt( diff --git a/llama_stack/providers/utils/inference/prompt_adapter.py b/llama_stack/providers/utils/inference/prompt_adapter.py index 7ee19fd7b..701b2ca3b 100644 --- a/llama_stack/providers/utils/inference/prompt_adapter.py +++ b/llama_stack/providers/utils/inference/prompt_adapter.py @@ -188,7 +188,7 @@ async def localize_image_content(media: ImageContentItem) -> Tuple[bytes, str]: async def convert_image_content_to_url( media: ImageContentItem, download: bool = False, include_format: bool = True ) -> str: - if media.url and not download: + if media.url and (not download or media.url.uri.startswith("data")): return media.url.uri content, format = await localize_image_content(media) From 5a63d0ff1d52cd7fd0fd32e27d1297a7a5ddc77a Mon Sep 17 00:00:00 2001 From: Yuan Tang Date: Sat, 18 Jan 2025 00:30:57 -0500 Subject: [PATCH 060/107] Fix incorrect RunConfigSettings due to the removal of conda_env (#801) --- llama_stack/templates/template.py | 1 - 1 file changed, 1 deletion(-) diff --git a/llama_stack/templates/template.py b/llama_stack/templates/template.py index d9696b23d..78f57b795 100644 --- a/llama_stack/templates/template.py +++ b/llama_stack/templates/template.py @@ -84,7 +84,6 @@ class RunConfigSettings(BaseModel): return StackRunConfig( image_name=name, container_image=container_image, - conda_env=name, apis=apis, providers=provider_configs, metadata_store=SqliteKVStoreConfig.sample_run_config( From 5379eca9fd60dd4068902d20b82a84f2b3285381 Mon Sep 17 00:00:00 2001 From: Yuan Tang Date: Sat, 18 Jan 2025 00:33:03 -0500 Subject: [PATCH 061/107] Fix incorrect image type in publish-to-docker workflow (#819) --- .github/workflows/publish-to-docker.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-to-docker.yml b/.github/workflows/publish-to-docker.yml index cf1e8b916..f63f52cbd 100644 --- a/.github/workflows/publish-to-docker.yml +++ b/.github/workflows/publish-to-docker.yml @@ -75,9 +75,9 @@ jobs: TEMPLATES=("ollama" "bedrock" "remote-vllm" "fireworks" "together" "tgi" "meta-reference-gpu") for template in "${TEMPLATES[@]}"; do if [ "$PYPI_SOURCE" = "testpypi" ]; then - TEST_PYPI_VERSION=${{ steps.version.outputs.version }} llama stack build --template $template --image-type docker + TEST_PYPI_VERSION=${{ steps.version.outputs.version }} llama stack build --template $template --image-type container else - PYPI_VERSION=${{ steps.version.outputs.version }} llama stack build --template $template --image-type docker + PYPI_VERSION=${{ steps.version.outputs.version }} llama stack build --template $template --image-type container fi done From 55067fa81df78ed7a28aa77e9b8d0676cd987f81 Mon Sep 17 00:00:00 2001 From: Sixian Yi Date: Sat, 18 Jan 2025 07:50:45 -0800 Subject: [PATCH 062/107] test report for v0.1 (#814) # What does this PR do? MD file for the test results of provider <> inference tests ## Test Plan 1) install `pip install pytest-md-report` 2) Run inference tests with the additions to the commands `--md-report --md-report-verbose=1 --md-report-output=tgi.md` Test text model: meta-llama/Llama-3.1-8B-Instruct Test vision model: meta-llama/Llama-3.2-11B-Vision-Instruct ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --------- Co-authored-by: Xi Yan --- llama_stack/providers/tests/test_report.md | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 llama_stack/providers/tests/test_report.md diff --git a/llama_stack/providers/tests/test_report.md b/llama_stack/providers/tests/test_report.md new file mode 100644 index 000000000..1153ef772 --- /dev/null +++ b/llama_stack/providers/tests/test_report.md @@ -0,0 +1,70 @@ +### Fireworks +| filepath | function | passed | SUBTOTAL | +| -------------------------------------------------------------- | ------------------------------------------------------------------ | -----: | -------: | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 1 | +| llama_stack/providers/tests/inference/test_vision_inference.py | TestVisionModelInference.test_vision_chat_completion_non_streaming | 2 | 2 | +| llama_stack/providers/tests/inference/test_vision_inference.py | TestVisionModelInference.test_vision_chat_completion_streaming | 1 | 1 | +| TOTAL | | 9 | 9 | + + + +### Together +| filepath | function | passed | SUBTOTAL | +| -------------------------------------------------------------- | ------------------------------------------------------------------ | -----: | -------: | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 1 | +| llama_stack/providers/tests/inference/test_vision_inference.py | TestVisionModelInference.test_vision_chat_completion_non_streaming | 2 | 2 | +| llama_stack/providers/tests/inference/test_vision_inference.py | TestVisionModelInference.test_vision_chat_completion_streaming | 1 | 1 | +| TOTAL | | 9 | 9 | + + +### vLLM + +| filepath | function | passed | skipped | SUBTOTAL | +| ------------------------------------------------------------ | -------------------------------------------------------------- | -----: | ------: | -------: | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_model_list | 1 | 0 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 0 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 0 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 0 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 0 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 0 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion | 0 | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion_logprobs | 0 | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion_structured_output | 0 | 1 | 1 | +| TOTAL | | 6 | 3 | 9 | + +### Ollama +| filepath | function | passed | SUBTOTAL | +| ------------------------------------------------------------ | -------------------------------------------------------------- | -----: | -------: | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 1 | +| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 1 | +| TOTAL | | 6 | 6 | + + +### tgi + +| filepath | function | passed | skipped | SUBTOTAL | +| ------------------------------------------------ | -------------------------------------------------------------- | -----: | ------: | -------: | +| providers/tests/inference/test_text_inference.py | TestInference.test_model_list | 1 | 0 | 1 | +| providers/tests/inference/test_text_inference.py | TestInference.test_completion | 1 | 0 | 1 | +| providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 0 | 1 | +| providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 0 | 1 | +| providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 0 | 1 | +| providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 0 | 1 | +| providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 0 | 1 | +| providers/tests/inference/test_text_inference.py | TestInference.test_completion_logprobs | 0 | 1 | 1 | +| providers/tests/inference/test_text_inference.py | TestInference.test_completion_structured_output | 0 | 1 | 1 | +| TOTAL | | 7 | 2 | 9 | From 74f6af8bbe64241e079ac613925071fa6e578505 Mon Sep 17 00:00:00 2001 From: Xi Yan Date: Sat, 18 Jan 2025 15:16:05 -0800 Subject: [PATCH 063/107] [CICD] add simple test step for docker build workflow, fix prefix bug (#821) # What does this PR do? **Main Thing** - Add a simple test step before publishing docker image in workflow **Side Fix** - Docker push action fails recently due to extra prefix introduced. E.g. see: https://github.com/meta-llama/llama-stack/pull/802#issuecomment-2599507062 cc @terrytangyuan ## Test Plan 1. Release a TestPyPi version on this code: 0.0.63.dev51206766 https://github.com/meta-llama/llama-stack/actions/runs/12841805606/job/35812033317?pr=821 ``` # 1. build docker image TEST_PYPI_VERSION=0.0.63.dev51206766 llama stack build --template fireworks # 2. test the docker image cd distributions/fireworks && docker compose up ``` 4. Test the full build + test docker flow using TestPyPi from (1): https://github.com/meta-llama/llama-stack/actions/runs/12842184947 image ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- .github/workflows/publish-to-docker.yml | 41 ++++++++++++++++++++- distributions/fireworks/compose.yaml | 8 ++-- distributions/together/compose.yaml | 8 ++-- llama_stack/distribution/build_container.sh | 3 +- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/.github/workflows/publish-to-docker.yml b/.github/workflows/publish-to-docker.yml index f63f52cbd..1010041b7 100644 --- a/.github/workflows/publish-to-docker.yml +++ b/.github/workflows/publish-to-docker.yml @@ -11,6 +11,10 @@ on: jobs: build-and-push: runs-on: ubuntu-latest + env: + TOGETHER_API_KEY: ${{ secrets.TOGETHER_API_KEY }} + FIREWORKS_API_KEY: ${{ secrets.FIREWORKS_API_KEY }} + TAVILY_SEARCH_API_KEY: ${{ secrets.TAVILY_SEARCH_API_KEY }} permissions: contents: read packages: write @@ -32,7 +36,7 @@ jobs: id: version run: | if [ "${{ github.event_name }}" = "push" ]; then - echo "VERSION=0.0.63.dev20250114" >> $GITHUB_OUTPUT + echo "VERSION=0.0.63.dev51206766" >> $GITHUB_OUTPUT else echo "VERSION=${{ inputs.version }}" >> $GITHUB_OUTPUT fi @@ -85,6 +89,41 @@ jobs: run: | docker images + # TODO (xiyan): make the following 2 steps into a matrix and test all templates other than fireworks + - name: Start up built docker image + run: | + cd distributions/fireworks + if [ "$PYPI_SOURCE" = "testpypi" ]; then + sed -i 's|image: llamastack/distribution-fireworks|image: distribution-fireworks:test-${{ steps.version.outputs.version }}|' ./compose.yaml + else + sed -i 's|image: llamastack/distribution-fireworks|image: distribution-fireworks:${{ steps.version.outputs.version }}|' ./compose.yaml + fi + docker compose up -d + cd .. + # Wait for the container to start + timeout=300 + while ! curl -s -f http://localhost:8321/v1/version > /dev/null && [ $timeout -gt 0 ]; do + echo "Waiting for endpoint to be available..." + sleep 5 + timeout=$((timeout - 5)) + done + + if [ $timeout -le 0 ]; then + echo "Timeout waiting for endpoint to become available" + exit 1 + fi + + - name: Run simple models list test on docker server + run: | + curl http://localhost:8321/v1/models + + # TODO (xiyan): figure out why client cannot find server but curl works + # - name: Run pytest on docker server + # run: | + # pip install pytest pytest-md-report + # export LLAMA_STACK_BASE_URL="http://localhost:8321" + # LLAMA_STACK_BASE_URL="http://localhost:8321" pytest -v tests/client-sdk/inference/test_inference.py --md-report --md-report-verbose=1 + - name: Push to dockerhub run: | TEMPLATES=("ollama" "bedrock" "remote-vllm" "fireworks" "together" "tgi" "meta-reference-gpu") diff --git a/distributions/fireworks/compose.yaml b/distributions/fireworks/compose.yaml index 4b53fcf00..84b8491e4 100644 --- a/distributions/fireworks/compose.yaml +++ b/distributions/fireworks/compose.yaml @@ -1,13 +1,11 @@ services: llamastack: image: llamastack/distribution-fireworks - network_mode: "host" - volumes: - - ~/.llama:/root/.llama - - ./run.yaml:/root/llamastack-run-fireworks.yaml ports: - "8321:8321" - entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml_config /root/llamastack-run-fireworks.yaml" + environment: + - FIREWORKS_API_KEY=${FIREWORKS_API_KEY} + entrypoint: bash -c "python -m llama_stack.distribution.server.server --template fireworks" deploy: restart_policy: condition: on-failure diff --git a/distributions/together/compose.yaml b/distributions/together/compose.yaml index c7251d0a7..f66ee69f9 100644 --- a/distributions/together/compose.yaml +++ b/distributions/together/compose.yaml @@ -1,13 +1,11 @@ services: llamastack: image: llamastack/distribution-together - network_mode: "host" - volumes: - - ~/.llama:/root/.llama - - ./run.yaml:/root/llamastack-run-together.yaml ports: - "8321:8321" - entrypoint: bash -c "python -m llama_stack.distribution.server.server --yaml_config /root/llamastack-run-together.yaml" + environment: + - TOGETHER_API_KEY=${TOGETHER_API_KEY} + entrypoint: bash -c "python -m llama_stack.distribution.server.server --template together" deploy: restart_policy: condition: on-failure diff --git a/llama_stack/distribution/build_container.sh b/llama_stack/distribution/build_container.sh index 4c2425004..c7b6211f7 100755 --- a/llama_stack/distribution/build_container.sh +++ b/llama_stack/distribution/build_container.sh @@ -23,7 +23,6 @@ special_pip_deps="$6" set -euo pipefail build_name="$1" -image_name="distribution-$build_name" container_base=$2 build_file_path=$3 host_build_dir=$4 @@ -184,7 +183,7 @@ else fi # Add version tag to image name -image_tag="$image_name:$version_tag" +image_tag="$build_name:$version_tag" # Detect platform architecture ARCH=$(uname -m) From 75a2694daac05b7fde7d840a510923e5373fb5e6 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Sun, 19 Jan 2025 12:22:40 -0800 Subject: [PATCH 064/107] Refactor the API enum to an independent file into llama_stack/apis/ --- llama_stack/apis/datatypes.py | 35 ++++++++++++++++++++++++++++++ llama_stack/providers/datatypes.py | 29 ++----------------------- 2 files changed, 37 insertions(+), 27 deletions(-) create mode 100644 llama_stack/apis/datatypes.py diff --git a/llama_stack/apis/datatypes.py b/llama_stack/apis/datatypes.py new file mode 100644 index 000000000..52c429a2b --- /dev/null +++ b/llama_stack/apis/datatypes.py @@ -0,0 +1,35 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +from enum import Enum + +from llama_models.schema_utils import json_schema_type + + +@json_schema_type +class Api(Enum): + inference = "inference" + safety = "safety" + agents = "agents" + memory = "memory" + datasetio = "datasetio" + scoring = "scoring" + eval = "eval" + post_training = "post_training" + tool_runtime = "tool_runtime" + + telemetry = "telemetry" + + models = "models" + shields = "shields" + memory_banks = "memory_banks" + datasets = "datasets" + scoring_functions = "scoring_functions" + eval_tasks = "eval_tasks" + tool_groups = "tool_groups" + + # built-in API + inspect = "inspect" diff --git a/llama_stack/providers/datatypes.py b/llama_stack/providers/datatypes.py index 94563879c..4815754d2 100644 --- a/llama_stack/providers/datatypes.py +++ b/llama_stack/providers/datatypes.py @@ -4,7 +4,6 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -from enum import Enum from typing import Any, List, Optional, Protocol from urllib.parse import urlparse @@ -12,6 +11,8 @@ from llama_models.schema_utils import json_schema_type from pydantic import BaseModel, Field from llama_stack.apis.datasets import Dataset + +from llama_stack.apis.datatypes import Api from llama_stack.apis.eval_tasks import EvalTask from llama_stack.apis.memory_banks.memory_banks import MemoryBank from llama_stack.apis.models import Model @@ -20,32 +21,6 @@ from llama_stack.apis.shields import Shield from llama_stack.apis.tools import Tool -@json_schema_type -class Api(Enum): - inference = "inference" - safety = "safety" - agents = "agents" - memory = "memory" - datasetio = "datasetio" - scoring = "scoring" - eval = "eval" - post_training = "post_training" - tool_runtime = "tool_runtime" - - telemetry = "telemetry" - - models = "models" - shields = "shields" - memory_banks = "memory_banks" - datasets = "datasets" - scoring_functions = "scoring_functions" - eval_tasks = "eval_tasks" - tool_groups = "tool_groups" - - # built-in API - inspect = "inspect" - - class ModelsProtocolPrivate(Protocol): async def register_model(self, model: Model) -> None: ... From 7a4b382ae93e5ea0166ef46c462593d28247861c Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Tue, 21 Jan 2025 13:10:42 -0800 Subject: [PATCH 065/107] add section for mcp tool usage in notebook (#831) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # What does this PR do? Adds a section to the notebook on how to use tools hosted in MCP server. ![Screenshot 2025-01-21 at 11 05 39 AM](https://github.com/user-attachments/assets/23e900f1-e2a7-4a46-be9b-13642753dca1) Notebook: https://colab.research.google.com/drive/1hBKX01NlG6p2BUrBU0ynwIlWjXQRxc3k?usp=sharing Rendered notebook on this branch: https://github.com/meta-llama/llama-stack/blob/mcp-notebook/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb --- ...Llama_Stack_Building_AI_Applications.ipynb | 9914 +++++++++-------- 1 file changed, 5470 insertions(+), 4444 deletions(-) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index df8995fd4..5857901bd 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -79,12 +79,12 @@ }, "collapsed": true, "id": "J2kGed0R5PSf", - "outputId": "3fa6d087-2f12-444f-b3d3-9331305abb51" + "outputId": "2478ea60-8d35-48a1-b011-f233831740c5" }, "outputs": [ { - "name": "stdout", "output_type": "stream", + "name": "stdout", "text": [ "Reading package lists... Done\n", "Building dependency tree... Done\n", @@ -95,147 +95,96 @@ "Need to get 46.3 kB of archives.\n", "After this operation, 132 kB of additional disk space will be used.\n", "Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 bubblewrap amd64 0.6.1-1ubuntu0.1 [46.3 kB]\n", - "Fetched 46.3 kB in 1s (52.2 kB/s)\n", + "Fetched 46.3 kB in 0s (122 kB/s)\n", "Selecting previously unselected package bubblewrap.\n", - "(Reading database ... 123632 files and directories currently installed.)\n", + "(Reading database ... 124561 files and directories currently installed.)\n", "Preparing to unpack .../bubblewrap_0.6.1-1ubuntu0.1_amd64.deb ...\n", "Unpacking bubblewrap (0.6.1-1ubuntu0.1) ...\n", "Setting up bubblewrap (0.6.1-1ubuntu0.1) ...\n", "Processing triggers for man-db (2.10.2-1) ...\n", - "Collecting llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git\n", - " Cloning https://github.com/meta-llama/llama-stack-client-python.git to /tmp/pip-install-y4g346dn/llama-stack-client_dea5c21edaf144f4b76e5cb6f78c1a79\n", - " Running command git clone --filter=blob:none --quiet https://github.com/meta-llama/llama-stack-client-python.git /tmp/pip-install-y4g346dn/llama-stack-client_dea5c21edaf144f4b76e5cb6f78c1a79\n", - " Resolved https://github.com/meta-llama/llama-stack-client-python.git to commit db90c54d82e3c2fa6f334adcaf700940dad163a3\n", - " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", - " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", - " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (3.7.1)\n", - "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (8.1.8)\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (1.9.0)\n", - "Requirement already satisfied: httpx<1,>=0.23.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (0.28.1)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2.2.2)\n", - "Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (3.0.48)\n", - "Collecting pyaml (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git)\n", - " Downloading pyaml-25.1.0-py3-none-any.whl.metadata (12 kB)\n", - "Requirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2.10.4)\n", - "Requirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (13.9.4)\n", - "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (1.3.1)\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2.5.0)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (4.67.1)\n", - "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (4.12.2)\n", - "Requirement already satisfied: idna>=2.8 in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (3.10)\n", - "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (1.2.2)\n", - "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2024.12.14)\n", - "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx<1,>=0.23.0->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (1.0.7)\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx<1,>=0.23.0->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (0.14.0)\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1.9.0->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (0.7.0)\n", - "Requirement already satisfied: pydantic-core==2.27.2 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1.9.0->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2.27.2)\n", - "Requirement already satisfied: numpy>=1.22.4 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (1.26.4)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2024.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2024.2)\n", - "Requirement already satisfied: wcwidth in /usr/local/lib/python3.10/dist-packages (from prompt-toolkit->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (0.2.13)\n", - "Requirement already satisfied: PyYAML in /usr/local/lib/python3.10/dist-packages (from pyaml->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (6.0.2)\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (3.0.0)\n", - "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (2.18.0)\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (0.1.2)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas->llama-stack-client@ git+https://github.com/meta-llama/llama-stack-client-python.git) (1.17.0)\n", - "Downloading pyaml-25.1.0-py3-none-any.whl (26 kB)\n", - "Building wheels for collected packages: llama-stack-client\n", - " Building wheel for llama-stack-client (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for llama-stack-client: filename=llama_stack_client-0.0.63-py3-none-any.whl size=318443 sha256=212ae3a9f3d5bb8a88801e4c3e625d99c9cb1d50d978cb6b2a8f7d069f013f06\n", - " Stored in directory: /tmp/pip-ephem-wheel-cache-c7a22578/wheels/c9/21/63/5f6965968ab3dae8a0b1a0e43ca4991732ca03184aa158c15c\n", - "Successfully built llama-stack-client\n", - "Installing collected packages: pyaml, llama-stack-client\n", - "Successfully installed llama-stack-client-0.0.63 pyaml-25.1.0\n", - "Collecting llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor\n", - " Cloning https://github.com/meta-llama/llama-stack.git (to revision fix_sqlite_span_processor) to /tmp/pip-install-0iqgax6t/llama-stack_824f45a9298043deacb6c11e12206393\n", - " Running command git clone --filter=blob:none --quiet https://github.com/meta-llama/llama-stack.git /tmp/pip-install-0iqgax6t/llama-stack_824f45a9298043deacb6c11e12206393\n", - " Running command git checkout -b fix_sqlite_span_processor --track origin/fix_sqlite_span_processor\n", - " Switched to a new branch 'fix_sqlite_span_processor'\n", - " Branch 'fix_sqlite_span_processor' set up to track remote branch 'fix_sqlite_span_processor' from 'origin'.\n", - " Resolved https://github.com/meta-llama/llama-stack.git to commit 6fc155f25261691613d075fd8d08f728c2596815\n", - " Running command git submodule update --init --recursive -q\n", - " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", - " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", - " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - "Collecting blobfile (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor)\n", + "Looking in indexes: https://test.pypi.org/simple/, https://pypi.python.org/simple\n", + "Collecting llama-stack==0.1.0rc10\n", + " Downloading https://test-files.pythonhosted.org/packages/68/22/4a170fbe01095df81e76c7bf8f35c716c1a0a5ec4503da6e78695fab351c/llama_stack-0.1.0rc10-py3-none-any.whl.metadata (15 kB)\n", + "Collecting blobfile (from llama-stack==0.1.0rc10)\n", " Downloading blobfile-3.0.0-py3-none-any.whl.metadata (15 kB)\n", - "Collecting fire (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor)\n", + "Collecting fire (from llama-stack==0.1.0rc10)\n", " Downloading fire-0.7.0.tar.gz (87 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m87.2/87.2 kB\u001b[0m \u001b[31m8.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m87.2/87.2 kB\u001b[0m \u001b[31m4.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: httpx in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (0.28.1)\n", - "Requirement already satisfied: huggingface-hub in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (0.27.1)\n", - "Collecting llama-models>=0.0.63 (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor)\n", - " Downloading llama_models-0.0.63-py3-none-any.whl.metadata (8.2 kB)\n", - "Requirement already satisfied: llama-stack-client>=0.0.63 in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (0.0.63)\n", - "Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (3.0.48)\n", - "Collecting python-dotenv (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor)\n", + "Requirement already satisfied: httpx in /usr/local/lib/python3.11/dist-packages (from llama-stack==0.1.0rc10) (0.28.1)\n", + "Requirement already satisfied: huggingface-hub in /usr/local/lib/python3.11/dist-packages (from llama-stack==0.1.0rc10) (0.27.1)\n", + "Collecting llama-models==0.1.0rc10 (from llama-stack==0.1.0rc10)\n", + " Downloading https://test-files.pythonhosted.org/packages/45/2b/6a6947d5915054b9980f82606942f1b79960a27168299254ca12e5b5795b/llama_models-0.1.0rc10-py3-none-any.whl.metadata (8.5 kB)\n", + "Collecting llama-stack-client==0.1.0rc10 (from llama-stack==0.1.0rc10)\n", + " Downloading https://test-files.pythonhosted.org/packages/d6/85/a4fd621c4ae4db7339ab098b37bf4b4ad3cc12440e75ef10ec524e28ef7d/llama_stack_client-0.1.0rc10-py3-none-any.whl.metadata (15 kB)\n", + "Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.11/dist-packages (from llama-stack==0.1.0rc10) (3.0.48)\n", + "Collecting python-dotenv (from llama-stack==0.1.0rc10)\n", " Downloading python_dotenv-1.0.1-py3-none-any.whl.metadata (23 kB)\n", - "Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2.10.4)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2.32.3)\n", - "Requirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (13.9.4)\n", - "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (75.1.0)\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2.5.0)\n", - "Requirement already satisfied: PyYAML in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (6.0.2)\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (3.1.5)\n", - "Collecting tiktoken (from llama-models>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor)\n", - " Downloading tiktoken-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.6 kB)\n", - "Requirement already satisfied: Pillow in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (11.1.0)\n", - "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (3.7.1)\n", - "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (8.1.8)\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (1.9.0)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2.2.2)\n", - "Requirement already satisfied: pyaml in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (25.1.0)\n", - "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (1.3.1)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (4.67.1)\n", - "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (4.12.2)\n", - "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2024.12.14)\n", - "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (1.0.7)\n", - "Requirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (3.10)\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (0.14.0)\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (0.7.0)\n", - "Requirement already satisfied: pydantic-core==2.27.2 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2.27.2)\n", - "Collecting pycryptodomex>=3.8 (from blobfile->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor)\n", + "Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.11/dist-packages (from llama-stack==0.1.0rc10) (2.10.5)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.11/dist-packages (from llama-stack==0.1.0rc10) (2.32.3)\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.11/dist-packages (from llama-stack==0.1.0rc10) (13.9.4)\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.11/dist-packages (from llama-stack==0.1.0rc10) (75.1.0)\n", + "Requirement already satisfied: termcolor in /usr/local/lib/python3.11/dist-packages (from llama-stack==0.1.0rc10) (2.5.0)\n", + "Requirement already satisfied: PyYAML in /usr/local/lib/python3.11/dist-packages (from llama-models==0.1.0rc10->llama-stack==0.1.0rc10) (6.0.2)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.11/dist-packages (from llama-models==0.1.0rc10->llama-stack==0.1.0rc10) (3.1.5)\n", + "Collecting tiktoken (from llama-models==0.1.0rc10->llama-stack==0.1.0rc10)\n", + " Downloading tiktoken-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.6 kB)\n", + "Requirement already satisfied: Pillow in /usr/local/lib/python3.11/dist-packages (from llama-models==0.1.0rc10->llama-stack==0.1.0rc10) (11.1.0)\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (3.7.1)\n", + "Requirement already satisfied: click in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (8.1.8)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (1.9.0)\n", + "Requirement already satisfied: pandas in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (2.2.2)\n", + "Collecting pyaml (from llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10)\n", + " Downloading pyaml-25.1.0-py3-none-any.whl.metadata (12 kB)\n", + "Requirement already satisfied: sniffio in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (1.3.1)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (4.67.1)\n", + "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (4.12.2)\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.11/dist-packages (from httpx->llama-stack==0.1.0rc10) (2024.12.14)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.11/dist-packages (from httpx->llama-stack==0.1.0rc10) (1.0.7)\n", + "Requirement already satisfied: idna in /usr/local/lib/python3.11/dist-packages (from httpx->llama-stack==0.1.0rc10) (3.10)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.11/dist-packages (from httpcore==1.*->httpx->llama-stack==0.1.0rc10) (0.14.0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.11/dist-packages (from pydantic>=2->llama-stack==0.1.0rc10) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.27.2 in /usr/local/lib/python3.11/dist-packages (from pydantic>=2->llama-stack==0.1.0rc10) (2.27.2)\n", + "Collecting pycryptodomex>=3.8 (from blobfile->llama-stack==0.1.0rc10)\n", " Downloading pycryptodomex-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.4 kB)\n", - "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2.3.0)\n", - "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (5.3.0)\n", - "Requirement already satisfied: filelock>=3.0 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (3.16.1)\n", - "Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2024.10.0)\n", - "Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (24.2)\n", - "Requirement already satisfied: wcwidth in /usr/local/lib/python3.10/dist-packages (from prompt-toolkit->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (0.2.13)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (3.4.1)\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (3.0.0)\n", - "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2.18.0)\n", - "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (1.2.2)\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (0.1.2)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->llama-models>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (3.0.2)\n", - "Requirement already satisfied: numpy>=1.22.4 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (1.26.4)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2024.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2024.2)\n", - "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.10/dist-packages (from tiktoken->llama-models>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (2024.11.6)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas->llama-stack-client>=0.0.63->llama-stack@ git+https://github.com/meta-llama/llama-stack.git@fix_sqlite_span_processor) (1.17.0)\n", - "Downloading llama_models-0.0.63-py3-none-any.whl (1.6 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/1.6 MB\u001b[0m \u001b[31m48.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.11/dist-packages (from blobfile->llama-stack==0.1.0rc10) (2.3.0)\n", + "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.11/dist-packages (from blobfile->llama-stack==0.1.0rc10) (5.3.0)\n", + "Requirement already satisfied: filelock>=3.0 in /usr/local/lib/python3.11/dist-packages (from blobfile->llama-stack==0.1.0rc10) (3.16.1)\n", + "Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.11/dist-packages (from huggingface-hub->llama-stack==0.1.0rc10) (2024.10.0)\n", + "Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.11/dist-packages (from huggingface-hub->llama-stack==0.1.0rc10) (24.2)\n", + "Requirement already satisfied: wcwidth in /usr/local/lib/python3.11/dist-packages (from prompt-toolkit->llama-stack==0.1.0rc10) (0.2.13)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests->llama-stack==0.1.0rc10) (3.4.1)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.11/dist-packages (from rich->llama-stack==0.1.0rc10) (3.0.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.11/dist-packages (from rich->llama-stack==0.1.0rc10) (2.18.0)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.11/dist-packages (from markdown-it-py>=2.2.0->rich->llama-stack==0.1.0rc10) (0.1.2)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.11/dist-packages (from jinja2->llama-models==0.1.0rc10->llama-stack==0.1.0rc10) (3.0.2)\n", + "Requirement already satisfied: numpy>=1.23.2 in /usr/local/lib/python3.11/dist-packages (from pandas->llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (1.26.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/dist-packages (from pandas->llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas->llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (2024.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas->llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (2024.2)\n", + "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.11/dist-packages (from tiktoken->llama-models==0.1.0rc10->llama-stack==0.1.0rc10) (2024.11.6)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil>=2.8.2->pandas->llama-stack-client==0.1.0rc10->llama-stack==0.1.0rc10) (1.17.0)\n", + "Downloading https://test-files.pythonhosted.org/packages/68/22/4a170fbe01095df81e76c7bf8f35c716c1a0a5ec4503da6e78695fab351c/llama_stack-0.1.0rc10-py3-none-any.whl (532 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m532.7/532.7 kB\u001b[0m \u001b[31m14.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading https://test-files.pythonhosted.org/packages/45/2b/6a6947d5915054b9980f82606942f1b79960a27168299254ca12e5b5795b/llama_models-0.1.0rc10-py3-none-any.whl (1.6 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/1.6 MB\u001b[0m \u001b[31m20.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading https://test-files.pythonhosted.org/packages/d6/85/a4fd621c4ae4db7339ab098b37bf4b4ad3cc12440e75ef10ec524e28ef7d/llama_stack_client-0.1.0rc10-py3-none-any.whl (328 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m328.5/328.5 kB\u001b[0m \u001b[31m29.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading blobfile-3.0.0-py3-none-any.whl (75 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.4/75.4 kB\u001b[0m \u001b[31m7.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.4/75.4 kB\u001b[0m \u001b[31m7.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading python_dotenv-1.0.1-py3-none-any.whl (19 kB)\n", "Downloading pycryptodomex-3.21.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.3 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.3/2.3 MB\u001b[0m \u001b[31m67.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading tiktoken-0.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.2 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.2/1.2 MB\u001b[0m \u001b[31m60.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hBuilding wheels for collected packages: llama-stack, fire\n", - " Building wheel for llama-stack (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for llama-stack: filename=llama_stack-0.0.63-py3-none-any.whl size=500660 sha256=36cd6d1b0146d456976f2d64deddf31a6515e5b0fbee97b61e448eb10356f3a7\n", - " Stored in directory: /tmp/pip-ephem-wheel-cache-qw3m4ho9/wheels/47/17/a3/49a8b1238e1c4640a5fdce6ad5055df118b069a670e77876e2\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.3/2.3 MB\u001b[0m \u001b[31m57.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading pyaml-25.1.0-py3-none-any.whl (26 kB)\n", + "Downloading tiktoken-0.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.2 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.2/1.2 MB\u001b[0m \u001b[31m64.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hBuilding wheels for collected packages: fire\n", " Building wheel for fire (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for fire: filename=fire-0.7.0-py3-none-any.whl size=114249 sha256=c1175a999f843dbb0dcabbeae06a6b080f59d7f78171dd089824c37fd63aeaef\n", - " Stored in directory: /root/.cache/pip/wheels/19/39/2f/2d3cadc408a8804103f1c34ddd4b9f6a93497b11fa96fe738e\n", - "Successfully built llama-stack fire\n", - "Installing collected packages: python-dotenv, pycryptodomex, fire, tiktoken, blobfile, llama-models, llama-stack\n", - "Successfully installed blobfile-3.0.0 fire-0.7.0 llama-models-0.0.63 llama-stack-0.0.63 pycryptodomex-3.21.0 python-dotenv-1.0.1 tiktoken-0.8.0\n" + " Created wheel for fire: filename=fire-0.7.0-py3-none-any.whl size=114249 sha256=3a37285ecae37a5fb69bbad717aabdb8c13f0da7906668b7c123475eefa41c3b\n", + " Stored in directory: /root/.cache/pip/wheels/46/54/24/1624fd5b8674eb1188623f7e8e17cdf7c0f6c24b609dfb8a89\n", + "Successfully built fire\n", + "Installing collected packages: python-dotenv, pycryptodomex, pyaml, fire, tiktoken, blobfile, llama-stack-client, llama-models, llama-stack\n", + "Successfully installed blobfile-3.0.0 fire-0.7.0 llama-models-0.1.0rc10 llama-stack-0.1.0rc10 llama-stack-client-0.1.0rc10 pyaml-25.1.0 pycryptodomex-3.21.0 python-dotenv-1.0.1 tiktoken-0.8.0\n" ] } ], @@ -277,263 +226,279 @@ }, "collapsed": true, "id": "HaepEZXCDgif", - "outputId": "6c983bb7-1cbe-4249-fd0a-0c629851981b" + "outputId": "9314f698-593d-4c1a-ea15-15c735dc1023" }, "outputs": [ { - "name": "stdout", "output_type": "stream", + "name": "stdout", "text": [ - "Requirement already satisfied: llama-stack in /usr/local/lib/python3.10/dist-packages (0.0.63)\r\n", - "Requirement already satisfied: blobfile in /usr/local/lib/python3.10/dist-packages (from llama-stack) (3.0.0)\r\n", - "Requirement already satisfied: fire in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.7.0)\r\n", - "Requirement already satisfied: httpx in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.28.1)\r\n", - "Requirement already satisfied: huggingface-hub in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.27.1)\r\n", - "Requirement already satisfied: llama-models>=0.0.63 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.0.63)\r\n", - "Requirement already satisfied: llama-stack-client>=0.0.63 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.0.63)\r\n", - "Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.10/dist-packages (from llama-stack) (3.0.48)\r\n", - "Requirement already satisfied: python-dotenv in /usr/local/lib/python3.10/dist-packages (from llama-stack) (1.0.1)\r\n", - "Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.10.4)\r\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.32.3)\r\n", - "Requirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (from llama-stack) (13.9.4)\r\n", - "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from llama-stack) (75.1.0)\r\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.5.0)\r\n", - "Requirement already satisfied: PyYAML in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.63->llama-stack) (6.0.2)\r\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.63->llama-stack) (3.1.5)\r\n", - "Requirement already satisfied: tiktoken in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.63->llama-stack) (0.8.0)\r\n", - "Requirement already satisfied: Pillow in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.63->llama-stack) (11.1.0)\r\n", - "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack) (3.7.1)\r\n", - "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack) (8.1.8)\r\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack) (1.9.0)\r\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack) (2.2.2)\r\n", - "Requirement already satisfied: pyaml in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack) (25.1.0)\r\n", - "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack) (1.3.1)\r\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack) (4.67.1)\r\n", - "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.63->llama-stack) (4.12.2)\r\n", - "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (2024.12.14)\r\n", - "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (1.0.7)\r\n", - "Requirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (3.10)\r\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx->llama-stack) (0.14.0)\r\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama-stack) (0.7.0)\r\n", - "Requirement already satisfied: pydantic-core==2.27.2 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama-stack) (2.27.2)\r\n", - "Requirement already satisfied: pycryptodomex>=3.8 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (3.21.0)\r\n", - "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (2.3.0)\r\n", - "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (5.3.0)\r\n", - "Requirement already satisfied: filelock>=3.0 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (3.16.1)\r\n", - "Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama-stack) (2024.10.0)\r\n", - "Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama-stack) (24.2)\r\n", - "Requirement already satisfied: wcwidth in /usr/local/lib/python3.10/dist-packages (from prompt-toolkit->llama-stack) (0.2.13)\r\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->llama-stack) (3.4.1)\r\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack) (3.0.0)\r\n", - "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack) (2.18.0)\r\n", - "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->llama-stack-client>=0.0.63->llama-stack) (1.2.2)\r\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich->llama-stack) (0.1.2)\r\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->llama-models>=0.0.63->llama-stack) (3.0.2)\n", - "Requirement already satisfied: numpy>=1.22.4 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.63->llama-stack) (1.26.4)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.63->llama-stack) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.63->llama-stack) (2024.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.63->llama-stack) (2024.2)\n", - "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.10/dist-packages (from tiktoken->llama-models>=0.0.63->llama-stack) (2024.11.6)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas->llama-stack-client>=0.0.63->llama-stack) (1.17.0)\n", + "Requirement already satisfied: llama-stack in /usr/local/lib/python3.11/dist-packages (0.1.0rc10)\r\n", + "Requirement already satisfied: blobfile in /usr/local/lib/python3.11/dist-packages (from llama-stack) (3.0.0)\r\n", + "Requirement already satisfied: fire in /usr/local/lib/python3.11/dist-packages (from llama-stack) (0.7.0)\r\n", + "Requirement already satisfied: httpx in /usr/local/lib/python3.11/dist-packages (from llama-stack) (0.28.1)\r\n", + "Requirement already satisfied: huggingface-hub in /usr/local/lib/python3.11/dist-packages (from llama-stack) (0.27.1)\r\n", + "Requirement already satisfied: llama-models==0.1.0rc10 in /usr/local/lib/python3.11/dist-packages (from llama-stack) (0.1.0rc10)\r\n", + "Requirement already satisfied: llama-stack-client==0.1.0rc10 in /usr/local/lib/python3.11/dist-packages (from llama-stack) (0.1.0rc10)\r\n", + "Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.11/dist-packages (from llama-stack) (3.0.48)\r\n", + "Requirement already satisfied: python-dotenv in /usr/local/lib/python3.11/dist-packages (from llama-stack) (1.0.1)\r\n", + "Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.11/dist-packages (from llama-stack) (2.10.5)\r\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.11/dist-packages (from llama-stack) (2.32.3)\r\n", + "Requirement already satisfied: rich in /usr/local/lib/python3.11/dist-packages (from llama-stack) (13.9.4)\r\n", + "Requirement already satisfied: setuptools in /usr/local/lib/python3.11/dist-packages (from llama-stack) (75.1.0)\r\n", + "Requirement already satisfied: termcolor in /usr/local/lib/python3.11/dist-packages (from llama-stack) (2.5.0)\r\n", + "Requirement already satisfied: PyYAML in /usr/local/lib/python3.11/dist-packages (from llama-models==0.1.0rc10->llama-stack) (6.0.2)\r\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.11/dist-packages (from llama-models==0.1.0rc10->llama-stack) (3.1.5)\r\n", + "Requirement already satisfied: tiktoken in /usr/local/lib/python3.11/dist-packages (from llama-models==0.1.0rc10->llama-stack) (0.8.0)\r\n", + "Requirement already satisfied: Pillow in /usr/local/lib/python3.11/dist-packages (from llama-models==0.1.0rc10->llama-stack) (11.1.0)\r\n", + "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack) (3.7.1)\r\n", + "Requirement already satisfied: click in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack) (8.1.8)\r\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack) (1.9.0)\r\n", + "Requirement already satisfied: pandas in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack) (2.2.2)\r\n", + "Requirement already satisfied: pyaml in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack) (25.1.0)\r\n", + "Requirement already satisfied: sniffio in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack) (1.3.1)\r\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack) (4.67.1)\r\n", + "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.11/dist-packages (from llama-stack-client==0.1.0rc10->llama-stack) (4.12.2)\r\n", + "Requirement already satisfied: certifi in /usr/local/lib/python3.11/dist-packages (from httpx->llama-stack) (2024.12.14)\r\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.11/dist-packages (from httpx->llama-stack) (1.0.7)\r\n", + "Requirement already satisfied: idna in /usr/local/lib/python3.11/dist-packages (from httpx->llama-stack) (3.10)\r\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.11/dist-packages (from httpcore==1.*->httpx->llama-stack) (0.14.0)\r\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.11/dist-packages (from pydantic>=2->llama-stack) (0.7.0)\r\n", + "Requirement already satisfied: pydantic-core==2.27.2 in /usr/local/lib/python3.11/dist-packages (from pydantic>=2->llama-stack) (2.27.2)\r\n", + "Requirement already satisfied: pycryptodomex>=3.8 in /usr/local/lib/python3.11/dist-packages (from blobfile->llama-stack) (3.21.0)\r\n", + "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.11/dist-packages (from blobfile->llama-stack) (2.3.0)\r\n", + "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.11/dist-packages (from blobfile->llama-stack) (5.3.0)\r\n", + "Requirement already satisfied: filelock>=3.0 in /usr/local/lib/python3.11/dist-packages (from blobfile->llama-stack) (3.16.1)\r\n", + "Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.11/dist-packages (from huggingface-hub->llama-stack) (2024.10.0)\r\n", + "Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.11/dist-packages (from huggingface-hub->llama-stack) (24.2)\r\n", + "Requirement already satisfied: wcwidth in /usr/local/lib/python3.11/dist-packages (from prompt-toolkit->llama-stack) (0.2.13)\r\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests->llama-stack) (3.4.1)\r\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.11/dist-packages (from rich->llama-stack) (3.0.0)\r\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.11/dist-packages (from rich->llama-stack) (2.18.0)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.11/dist-packages (from markdown-it-py>=2.2.0->rich->llama-stack) (0.1.2)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.11/dist-packages (from jinja2->llama-models==0.1.0rc10->llama-stack) (3.0.2)\n", + "Requirement already satisfied: numpy>=1.23.2 in /usr/local/lib/python3.11/dist-packages (from pandas->llama-stack-client==0.1.0rc10->llama-stack) (1.26.4)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/dist-packages (from pandas->llama-stack-client==0.1.0rc10->llama-stack) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas->llama-stack-client==0.1.0rc10->llama-stack) (2024.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas->llama-stack-client==0.1.0rc10->llama-stack) (2024.2)\n", + "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.11/dist-packages (from tiktoken->llama-models==0.1.0rc10->llama-stack) (2024.11.6)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from python-dateutil>=2.8.2->pandas->llama-stack-client==0.1.0rc10->llama-stack) (1.17.0)\n", "Installing pip dependencies\n", - "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.10/dist-packages (1.6.0)\n", - "Collecting psycopg2-binary\n", - " Downloading psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.9 kB)\n", - "Collecting autoevals\n", - " Downloading autoevals-0.0.115-py3-none-any.whl.metadata (12 kB)\n", - "Requirement already satisfied: scipy in /usr/local/lib/python3.10/dist-packages (1.13.1)\n", - "Collecting pypdf\n", - " Downloading pypdf-5.1.0-py3-none-any.whl.metadata (7.2 kB)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (2.2.2)\n", - "Collecting datasets\n", - " Downloading datasets-3.2.0-py3-none-any.whl.metadata (20 kB)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (4.67.1)\n", - "Requirement already satisfied: opentelemetry-sdk in /usr/local/lib/python3.10/dist-packages (1.29.0)\n", - "Requirement already satisfied: openai in /usr/local/lib/python3.10/dist-packages (1.59.4)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (2.32.3)\n", - "Collecting opentelemetry-exporter-otlp-proto-http\n", - " Downloading opentelemetry_exporter_otlp_proto_http-1.29.0-py3-none-any.whl.metadata (2.2 kB)\n", - "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (1.26.4)\n", + "Requirement already satisfied: pandas in /usr/local/lib/python3.11/dist-packages (2.2.2)\n", "Collecting together\n", " Downloading together-1.3.11-py3-none-any.whl.metadata (11 kB)\n", - "Requirement already satisfied: transformers in /usr/local/lib/python3.10/dist-packages (4.47.1)\n", - "Requirement already satisfied: chardet in /usr/local/lib/python3.10/dist-packages (5.2.0)\n", - "Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (3.10.0)\n", - "Requirement already satisfied: pillow in /usr/local/lib/python3.10/dist-packages (11.1.0)\n", - "Collecting faiss-cpu\n", - " Downloading faiss_cpu-1.9.0.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.4 kB)\n", - "Requirement already satisfied: sentencepiece in /usr/local/lib/python3.10/dist-packages (0.2.0)\n", + "Collecting datasets\n", + " Downloading datasets-3.2.0-py3-none-any.whl.metadata (20 kB)\n", + "Requirement already satisfied: transformers in /usr/local/lib/python3.11/dist-packages (4.47.1)\n", + "Requirement already satisfied: blobfile in /usr/local/lib/python3.11/dist-packages (3.0.0)\n", + "Requirement already satisfied: opentelemetry-sdk in /usr/local/lib/python3.11/dist-packages (1.29.0)\n", "Collecting redis\n", " Downloading redis-5.2.1-py3-none-any.whl.metadata (9.1 kB)\n", - "Requirement already satisfied: nltk in /usr/local/lib/python3.10/dist-packages (3.9.1)\n", + "Requirement already satisfied: matplotlib in /usr/local/lib/python3.11/dist-packages (3.10.0)\n", + "Requirement already satisfied: requests in /usr/local/lib/python3.11/dist-packages (2.32.3)\n", + "Requirement already satisfied: chardet in /usr/local/lib/python3.11/dist-packages (5.2.0)\n", "Collecting chromadb-client\n", - " Downloading chromadb_client-0.6.2-py3-none-any.whl.metadata (2.4 kB)\n", - "Requirement already satisfied: blobfile in /usr/local/lib/python3.10/dist-packages (3.0.0)\n", + " Downloading chromadb_client-0.6.3-py3-none-any.whl.metadata (2.4 kB)\n", + "Collecting psycopg2-binary\n", + " Downloading psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.9 kB)\n", + "Collecting mcp\n", + " Downloading mcp-1.2.0-py3-none-any.whl.metadata (15 kB)\n", + "Requirement already satisfied: pillow in /usr/local/lib/python3.11/dist-packages (11.1.0)\n", + "Requirement already satisfied: scipy in /usr/local/lib/python3.11/dist-packages (1.13.1)\n", + "Requirement already satisfied: tqdm in /usr/local/lib/python3.11/dist-packages (4.67.1)\n", + "Requirement already satisfied: nltk in /usr/local/lib/python3.11/dist-packages (3.9.1)\n", + "Requirement already satisfied: sentencepiece in /usr/local/lib/python3.11/dist-packages (0.2.0)\n", + "Collecting faiss-cpu\n", + " Downloading faiss_cpu-1.9.0.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.4 kB)\n", + "Collecting opentelemetry-exporter-otlp-proto-http\n", + " Downloading opentelemetry_exporter_otlp_proto_http-1.29.0-py3-none-any.whl.metadata (2.2 kB)\n", + "Collecting autoevals\n", + " Downloading autoevals-0.0.117-py3-none-any.whl.metadata (12 kB)\n", + "Collecting pypdf\n", + " Downloading pypdf-5.1.0-py3-none-any.whl.metadata (7.2 kB)\n", "Collecting aiosqlite\n", " Downloading aiosqlite-0.20.0-py3-none-any.whl.metadata (4.3 kB)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.11/dist-packages (1.26.4)\n", + "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.11/dist-packages (1.6.0)\n", + "Requirement already satisfied: openai in /usr/local/lib/python3.11/dist-packages (1.59.6)\n", "Collecting fastapi\n", " Downloading fastapi-0.115.6-py3-none-any.whl.metadata (27 kB)\n", - "Requirement already satisfied: fire in /usr/local/lib/python3.10/dist-packages (0.7.0)\n", - "Requirement already satisfied: httpx in /usr/local/lib/python3.10/dist-packages (0.28.1)\n", + "Requirement already satisfied: fire in /usr/local/lib/python3.11/dist-packages (0.7.0)\n", + "Requirement already satisfied: httpx in /usr/local/lib/python3.11/dist-packages (0.28.1)\n", "Collecting uvicorn\n", " Downloading uvicorn-0.34.0-py3-none-any.whl.metadata (6.5 kB)\n", - "Requirement already satisfied: joblib>=1.2.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (1.4.2)\n", - "Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (3.5.0)\n", - "Collecting chevron (from autoevals)\n", - " Downloading chevron-0.14.0-py3-none-any.whl.metadata (4.9 kB)\n", - "Collecting levenshtein (from autoevals)\n", - " Downloading levenshtein-0.26.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.2 kB)\n", - "Requirement already satisfied: pyyaml in /usr/local/lib/python3.10/dist-packages (from autoevals) (6.0.2)\n", - "Collecting braintrust_core==0.0.57 (from autoevals)\n", - " Downloading braintrust_core-0.0.57-py3-none-any.whl.metadata (669 bytes)\n", - "Requirement already satisfied: jsonschema in /usr/local/lib/python3.10/dist-packages (from autoevals) (4.23.0)\n", - "Requirement already satisfied: typing_extensions>=4.0 in /usr/local/lib/python3.10/dist-packages (from pypdf) (4.12.2)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas) (2024.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas) (2024.2)\n", - "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from datasets) (3.16.1)\n", - "Requirement already satisfied: pyarrow>=15.0.0 in /usr/local/lib/python3.10/dist-packages (from datasets) (17.0.0)\n", + "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/dist-packages (from pandas) (2.8.2)\n", + "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas) (2024.2)\n", + "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas) (2024.2)\n", + "Requirement already satisfied: aiohttp<4.0.0,>=3.9.3 in /usr/local/lib/python3.11/dist-packages (from together) (3.11.11)\n", + "Requirement already satisfied: click<9.0.0,>=8.1.7 in /usr/local/lib/python3.11/dist-packages (from together) (8.1.8)\n", + "Requirement already satisfied: eval-type-backport<0.3.0,>=0.1.3 in /usr/local/lib/python3.11/dist-packages (from together) (0.2.2)\n", + "Requirement already satisfied: filelock<4.0.0,>=3.13.1 in /usr/local/lib/python3.11/dist-packages (from together) (3.16.1)\n", + "Collecting pillow\n", + " Downloading pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl.metadata (9.2 kB)\n", + "Requirement already satisfied: pyarrow>=10.0.1 in /usr/local/lib/python3.11/dist-packages (from together) (17.0.0)\n", + "Requirement already satisfied: pydantic<3.0.0,>=2.6.3 in /usr/local/lib/python3.11/dist-packages (from together) (2.10.5)\n", + "Requirement already satisfied: rich<14.0.0,>=13.8.1 in /usr/local/lib/python3.11/dist-packages (from together) (13.9.4)\n", + "Requirement already satisfied: tabulate<0.10.0,>=0.9.0 in /usr/local/lib/python3.11/dist-packages (from together) (0.9.0)\n", + "Requirement already satisfied: typer<0.16,>=0.9 in /usr/local/lib/python3.11/dist-packages (from together) (0.15.1)\n", "Collecting dill<0.3.9,>=0.3.0 (from datasets)\n", " Downloading dill-0.3.8-py3-none-any.whl.metadata (10 kB)\n", "Collecting xxhash (from datasets)\n", - " Downloading xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (12 kB)\n", + " Downloading xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (12 kB)\n", "Collecting multiprocess<0.70.17 (from datasets)\n", - " Downloading multiprocess-0.70.16-py310-none-any.whl.metadata (7.2 kB)\n", + " Downloading multiprocess-0.70.16-py311-none-any.whl.metadata (7.2 kB)\n", "Collecting fsspec<=2024.9.0,>=2023.1.0 (from fsspec[http]<=2024.9.0,>=2023.1.0->datasets)\n", " Downloading fsspec-2024.9.0-py3-none-any.whl.metadata (11 kB)\n", - "Requirement already satisfied: aiohttp in /usr/local/lib/python3.10/dist-packages (from datasets) (3.11.11)\n", - "Requirement already satisfied: huggingface-hub>=0.23.0 in /usr/local/lib/python3.10/dist-packages (from datasets) (0.27.1)\n", - "Requirement already satisfied: packaging in /usr/local/lib/python3.10/dist-packages (from datasets) (24.2)\n", - "Requirement already satisfied: opentelemetry-api==1.29.0 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-sdk) (1.29.0)\n", - "Requirement already satisfied: opentelemetry-semantic-conventions==0.50b0 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-sdk) (0.50b0)\n", - "Requirement already satisfied: deprecated>=1.2.6 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-api==1.29.0->opentelemetry-sdk) (1.2.15)\n", - "Requirement already satisfied: importlib-metadata<=8.5.0,>=6.0 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-api==1.29.0->opentelemetry-sdk) (8.5.0)\n", - "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai) (3.7.1)\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from openai) (1.9.0)\n", - "Requirement already satisfied: jiter<1,>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from openai) (0.8.2)\n", - "Requirement already satisfied: pydantic<3,>=1.9.0 in /usr/local/lib/python3.10/dist-packages (from openai) (2.10.4)\n", - "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai) (1.3.1)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests) (3.4.1)\n", - "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests) (3.10)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests) (2.3.0)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests) (2024.12.14)\n", - "Requirement already satisfied: googleapis-common-protos~=1.52 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-exporter-otlp-proto-http) (1.66.0)\n", + "Requirement already satisfied: huggingface-hub>=0.23.0 in /usr/local/lib/python3.11/dist-packages (from datasets) (0.27.1)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.11/dist-packages (from datasets) (24.2)\n", + "Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.11/dist-packages (from datasets) (6.0.2)\n", + "Requirement already satisfied: regex!=2019.12.17 in /usr/local/lib/python3.11/dist-packages (from transformers) (2024.11.6)\n", + "Requirement already satisfied: tokenizers<0.22,>=0.21 in /usr/local/lib/python3.11/dist-packages (from transformers) (0.21.0)\n", + "Requirement already satisfied: safetensors>=0.4.1 in /usr/local/lib/python3.11/dist-packages (from transformers) (0.5.2)\n", + "Requirement already satisfied: pycryptodomex>=3.8 in /usr/local/lib/python3.11/dist-packages (from blobfile) (3.21.0)\n", + "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.11/dist-packages (from blobfile) (2.3.0)\n", + "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.11/dist-packages (from blobfile) (5.3.0)\n", + "Requirement already satisfied: opentelemetry-api==1.29.0 in /usr/local/lib/python3.11/dist-packages (from opentelemetry-sdk) (1.29.0)\n", + "Requirement already satisfied: opentelemetry-semantic-conventions==0.50b0 in /usr/local/lib/python3.11/dist-packages (from opentelemetry-sdk) (0.50b0)\n", + "Requirement already satisfied: typing-extensions>=3.7.4 in /usr/local/lib/python3.11/dist-packages (from opentelemetry-sdk) (4.12.2)\n", + "Requirement already satisfied: deprecated>=1.2.6 in /usr/local/lib/python3.11/dist-packages (from opentelemetry-api==1.29.0->opentelemetry-sdk) (1.2.15)\n", + "Requirement already satisfied: importlib-metadata<=8.5.0,>=6.0 in /usr/local/lib/python3.11/dist-packages (from opentelemetry-api==1.29.0->opentelemetry-sdk) (8.5.0)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (1.3.1)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (0.12.1)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (4.55.3)\n", + "Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (1.4.8)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (3.2.1)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests) (3.4.1)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests) (3.10)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests) (2024.12.14)\n", + "Collecting opentelemetry-exporter-otlp-proto-grpc>=1.2.0 (from chromadb-client)\n", + " Downloading opentelemetry_exporter_otlp_proto_grpc-1.29.0-py3-none-any.whl.metadata (2.2 kB)\n", + "Collecting overrides>=7.3.1 (from chromadb-client)\n", + " Downloading overrides-7.7.0-py3-none-any.whl.metadata (5.8 kB)\n", + "Collecting posthog>=2.4.0 (from chromadb-client)\n", + " Downloading posthog-3.8.4-py2.py3-none-any.whl.metadata (2.8 kB)\n", + "Requirement already satisfied: tenacity>=8.2.3 in /usr/local/lib/python3.11/dist-packages (from chromadb-client) (9.0.0)\n", + "Requirement already satisfied: orjson>=3.9.12 in /usr/local/lib/python3.11/dist-packages (from chromadb-client) (3.10.14)\n", + "Collecting anyio>=4.5 (from mcp)\n", + " Downloading anyio-4.8.0-py3-none-any.whl.metadata (4.6 kB)\n", + "Collecting httpx-sse>=0.4 (from mcp)\n", + " Downloading httpx_sse-0.4.0-py3-none-any.whl.metadata (9.0 kB)\n", + "Collecting pydantic-settings>=2.6.1 (from mcp)\n", + " Downloading pydantic_settings-2.7.1-py3-none-any.whl.metadata (3.5 kB)\n", + "Collecting sse-starlette>=1.6.1 (from mcp)\n", + " Downloading sse_starlette-2.2.1-py3-none-any.whl.metadata (7.8 kB)\n", + "Collecting starlette>=0.27 (from mcp)\n", + " Downloading starlette-0.45.2-py3-none-any.whl.metadata (6.3 kB)\n", + "Requirement already satisfied: joblib in /usr/local/lib/python3.11/dist-packages (from nltk) (1.4.2)\n", + "Requirement already satisfied: googleapis-common-protos~=1.52 in /usr/local/lib/python3.11/dist-packages (from opentelemetry-exporter-otlp-proto-http) (1.66.0)\n", "Collecting opentelemetry-exporter-otlp-proto-common==1.29.0 (from opentelemetry-exporter-otlp-proto-http)\n", " Downloading opentelemetry_exporter_otlp_proto_common-1.29.0-py3-none-any.whl.metadata (1.8 kB)\n", "Collecting opentelemetry-proto==1.29.0 (from opentelemetry-exporter-otlp-proto-http)\n", " Downloading opentelemetry_proto-1.29.0-py3-none-any.whl.metadata (2.3 kB)\n", "Collecting protobuf<6.0,>=5.0 (from opentelemetry-proto==1.29.0->opentelemetry-exporter-otlp-proto-http)\n", " Downloading protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl.metadata (592 bytes)\n", - "Requirement already satisfied: click<9.0.0,>=8.1.7 in /usr/local/lib/python3.10/dist-packages (from together) (8.1.8)\n", - "Requirement already satisfied: eval-type-backport<0.3.0,>=0.1.3 in /usr/local/lib/python3.10/dist-packages (from together) (0.2.2)\n", - "Collecting pillow\n", - " Downloading pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl.metadata (9.2 kB)\n", - "Requirement already satisfied: rich<14.0.0,>=13.8.1 in /usr/local/lib/python3.10/dist-packages (from together) (13.9.4)\n", - "Requirement already satisfied: tabulate<0.10.0,>=0.9.0 in /usr/local/lib/python3.10/dist-packages (from together) (0.9.0)\n", - "Requirement already satisfied: typer<0.16,>=0.9 in /usr/local/lib/python3.10/dist-packages (from together) (0.15.1)\n", - "Requirement already satisfied: regex!=2019.12.17 in /usr/local/lib/python3.10/dist-packages (from transformers) (2024.11.6)\n", - "Requirement already satisfied: tokenizers<0.22,>=0.21 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.21.0)\n", - "Requirement already satisfied: safetensors>=0.4.1 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.5.1)\n", - "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.3.1)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (0.12.1)\n", - "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (4.55.3)\n", - "Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.4.8)\n", - "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (3.2.1)\n", - "Requirement already satisfied: async-timeout>=4.0.3 in /usr/local/lib/python3.10/dist-packages (from redis) (4.0.3)\n", - "Collecting opentelemetry-exporter-otlp-proto-grpc>=1.2.0 (from chromadb-client)\n", - " Downloading opentelemetry_exporter_otlp_proto_grpc-1.29.0-py3-none-any.whl.metadata (2.2 kB)\n", - "Collecting overrides>=7.3.1 (from chromadb-client)\n", - " Downloading overrides-7.7.0-py3-none-any.whl.metadata (5.8 kB)\n", - "Collecting posthog>=2.4.0 (from chromadb-client)\n", - " Downloading posthog-3.7.5-py2.py3-none-any.whl.metadata (2.0 kB)\n", - "Requirement already satisfied: tenacity>=8.2.3 in /usr/local/lib/python3.10/dist-packages (from chromadb-client) (9.0.0)\n", - "Requirement already satisfied: orjson>=3.9.12 in /usr/local/lib/python3.10/dist-packages (from chromadb-client) (3.10.13)\n", - "Requirement already satisfied: pycryptodomex>=3.8 in /usr/local/lib/python3.10/dist-packages (from blobfile) (3.21.0)\n", - "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.10/dist-packages (from blobfile) (5.3.0)\n", - "Collecting starlette<0.42.0,>=0.40.0 (from fastapi)\n", + "Collecting chevron (from autoevals)\n", + " Downloading chevron-0.14.0-py3-none-any.whl.metadata (4.9 kB)\n", + "Collecting levenshtein (from autoevals)\n", + " Downloading levenshtein-0.26.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.2 kB)\n", + "Collecting braintrust_core==0.0.58 (from autoevals)\n", + " Downloading braintrust_core-0.0.58-py3-none-any.whl.metadata (669 bytes)\n", + "Requirement already satisfied: jsonschema in /usr/local/lib/python3.11/dist-packages (from autoevals) (4.23.0)\n", + "Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.11/dist-packages (from scikit-learn) (3.5.0)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.11/dist-packages (from openai) (1.9.0)\n", + "Requirement already satisfied: jiter<1,>=0.4.0 in /usr/local/lib/python3.11/dist-packages (from openai) (0.8.2)\n", + "Requirement already satisfied: sniffio in /usr/local/lib/python3.11/dist-packages (from openai) (1.3.1)\n", + "Collecting starlette>=0.27 (from mcp)\n", " Downloading starlette-0.41.3-py3-none-any.whl.metadata (6.0 kB)\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from fire) (2.5.0)\n", - "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx) (1.0.7)\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx) (0.14.0)\n", - "Requirement already satisfied: aiohappyeyeballs>=2.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets) (2.4.4)\n", - "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets) (1.3.2)\n", - "Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets) (24.3.0)\n", - "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets) (1.5.0)\n", - "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets) (6.1.0)\n", - "Requirement already satisfied: propcache>=0.2.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets) (0.2.1)\n", - "Requirement already satisfied: yarl<2.0,>=1.17.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp->datasets) (1.18.3)\n", - "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai) (1.2.2)\n", - "Requirement already satisfied: wrapt<2,>=1.10 in /usr/local/lib/python3.10/dist-packages (from deprecated>=1.2.6->opentelemetry-api==1.29.0->opentelemetry-sdk) (1.17.0)\n", - "Requirement already satisfied: grpcio<2.0.0,>=1.63.2 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb-client) (1.69.0)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from posthog>=2.4.0->chromadb-client) (1.17.0)\n", + "Requirement already satisfied: termcolor in /usr/local/lib/python3.11/dist-packages (from fire) (2.5.0)\n", + "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.11/dist-packages (from httpx) (1.0.7)\n", + "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.11/dist-packages (from httpcore==1.*->httpx) (0.14.0)\n", + "Requirement already satisfied: aiohappyeyeballs>=2.3.0 in /usr/local/lib/python3.11/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (2.4.4)\n", + "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.11/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (1.3.2)\n", + "Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.11/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (24.3.0)\n", + "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.11/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (1.5.0)\n", + "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.11/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (6.1.0)\n", + "Requirement already satisfied: propcache>=0.2.0 in /usr/local/lib/python3.11/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (0.2.1)\n", + "Requirement already satisfied: yarl<2.0,>=1.17.0 in /usr/local/lib/python3.11/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (1.18.3)\n", + "Requirement already satisfied: wrapt<2,>=1.10 in /usr/local/lib/python3.11/dist-packages (from deprecated>=1.2.6->opentelemetry-api==1.29.0->opentelemetry-sdk) (1.17.0)\n", + "Requirement already satisfied: grpcio<2.0.0,>=1.63.2 in /usr/local/lib/python3.11/dist-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb-client) (1.69.0)\n", + "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.11/dist-packages (from posthog>=2.4.0->chromadb-client) (1.17.0)\n", "Collecting monotonic>=1.5 (from posthog>=2.4.0->chromadb-client)\n", " Downloading monotonic-1.6-py2.py3-none-any.whl.metadata (1.5 kB)\n", "Collecting backoff>=1.10.0 (from posthog>=2.4.0->chromadb-client)\n", " Downloading backoff-2.2.1-py3-none-any.whl.metadata (14 kB)\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1.9.0->openai) (0.7.0)\n", - "Requirement already satisfied: pydantic-core==2.27.2 in /usr/local/lib/python3.10/dist-packages (from pydantic<3,>=1.9.0->openai) (2.27.2)\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich<14.0.0,>=13.8.1->together) (3.0.0)\n", - "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich<14.0.0,>=13.8.1->together) (2.18.0)\n", - "Requirement already satisfied: shellingham>=1.3.0 in /usr/local/lib/python3.10/dist-packages (from typer<0.16,>=0.9->together) (1.5.4)\n", - "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.10/dist-packages (from jsonschema->autoevals) (2024.10.1)\n", - "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.10/dist-packages (from jsonschema->autoevals) (0.35.1)\n", - "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.10/dist-packages (from jsonschema->autoevals) (0.22.3)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.11/dist-packages (from pydantic<3.0.0,>=2.6.3->together) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.27.2 in /usr/local/lib/python3.11/dist-packages (from pydantic<3.0.0,>=2.6.3->together) (2.27.2)\n", + "Requirement already satisfied: python-dotenv>=0.21.0 in /usr/local/lib/python3.11/dist-packages (from pydantic-settings>=2.6.1->mcp) (1.0.1)\n", + "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.11/dist-packages (from rich<14.0.0,>=13.8.1->together) (3.0.0)\n", + "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.11/dist-packages (from rich<14.0.0,>=13.8.1->together) (2.18.0)\n", + "Requirement already satisfied: shellingham>=1.3.0 in /usr/local/lib/python3.11/dist-packages (from typer<0.16,>=0.9->together) (1.5.4)\n", + "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.11/dist-packages (from jsonschema->autoevals) (2024.10.1)\n", + "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.11/dist-packages (from jsonschema->autoevals) (0.35.1)\n", + "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.11/dist-packages (from jsonschema->autoevals) (0.22.3)\n", "Collecting rapidfuzz<4.0.0,>=3.9.0 (from levenshtein->autoevals)\n", - " Downloading rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (11 kB)\n", - "Requirement already satisfied: zipp>=3.20 in /usr/local/lib/python3.10/dist-packages (from importlib-metadata<=8.5.0,>=6.0->opentelemetry-api==1.29.0->opentelemetry-sdk) (3.21.0)\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich<14.0.0,>=13.8.1->together) (0.1.2)\n", - "Downloading psycopg2_binary-2.9.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.0/3.0 MB\u001b[0m \u001b[31m84.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading autoevals-0.0.115-py3-none-any.whl (41 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.1/41.1 kB\u001b[0m \u001b[31m3.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading braintrust_core-0.0.57-py3-none-any.whl (4.4 kB)\n", - "Downloading pypdf-5.1.0-py3-none-any.whl (297 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m298.0/298.0 kB\u001b[0m \u001b[31m29.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + " Downloading rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (11 kB)\n", + "Requirement already satisfied: zipp>=3.20 in /usr/local/lib/python3.11/dist-packages (from importlib-metadata<=8.5.0,>=6.0->opentelemetry-api==1.29.0->opentelemetry-sdk) (3.21.0)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.11/dist-packages (from markdown-it-py>=2.2.0->rich<14.0.0,>=13.8.1->together) (0.1.2)\n", + "Downloading together-1.3.11-py3-none-any.whl (70 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m70.6/70.6 kB\u001b[0m \u001b[31m7.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading datasets-3.2.0-py3-none-any.whl (480 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m480.6/480.6 kB\u001b[0m \u001b[31m37.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m480.6/480.6 kB\u001b[0m \u001b[31m20.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading redis-5.2.1-py3-none-any.whl (261 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m261.5/261.5 kB\u001b[0m \u001b[31m25.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading chromadb_client-0.6.3-py3-none-any.whl (609 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m609.2/609.2 kB\u001b[0m \u001b[31m38.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading psycopg2_binary-2.9.10-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.0 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.0/3.0 MB\u001b[0m \u001b[31m100.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading mcp-1.2.0-py3-none-any.whl (66 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m66.5/66.5 kB\u001b[0m \u001b[31m7.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl (4.5 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.5/4.5 MB\u001b[0m \u001b[31m106.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading faiss_cpu-1.9.0.post1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (27.5 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m27.5/27.5 MB\u001b[0m \u001b[31m78.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading opentelemetry_exporter_otlp_proto_http-1.29.0-py3-none-any.whl (17 kB)\n", "Downloading opentelemetry_exporter_otlp_proto_common-1.29.0-py3-none-any.whl (18 kB)\n", "Downloading opentelemetry_proto-1.29.0-py3-none-any.whl (55 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m55.8/55.8 kB\u001b[0m \u001b[31m5.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading together-1.3.11-py3-none-any.whl (70 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m70.6/70.6 kB\u001b[0m \u001b[31m6.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl (4.5 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m4.5/4.5 MB\u001b[0m \u001b[31m105.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading faiss_cpu-1.9.0.post1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (27.5 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m27.5/27.5 MB\u001b[0m \u001b[31m78.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading redis-5.2.1-py3-none-any.whl (261 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m261.5/261.5 kB\u001b[0m \u001b[31m23.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading chromadb_client-0.6.2-py3-none-any.whl (604 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m604.2/604.2 kB\u001b[0m \u001b[31m47.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m55.8/55.8 kB\u001b[0m \u001b[31m4.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading autoevals-0.0.117-py3-none-any.whl (41 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.4/41.4 kB\u001b[0m \u001b[31m4.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading braintrust_core-0.0.58-py3-none-any.whl (4.4 kB)\n", + "Downloading pypdf-5.1.0-py3-none-any.whl (297 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m298.0/298.0 kB\u001b[0m \u001b[31m24.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading aiosqlite-0.20.0-py3-none-any.whl (15 kB)\n", "Downloading fastapi-0.115.6-py3-none-any.whl (94 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m94.8/94.8 kB\u001b[0m \u001b[31m9.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m94.8/94.8 kB\u001b[0m \u001b[31m9.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading uvicorn-0.34.0-py3-none-any.whl (62 kB)\n", "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m62.3/62.3 kB\u001b[0m \u001b[31m5.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading anyio-4.8.0-py3-none-any.whl (96 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m96.0/96.0 kB\u001b[0m \u001b[31m9.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading dill-0.3.8-py3-none-any.whl (116 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m116.3/116.3 kB\u001b[0m \u001b[31m9.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m116.3/116.3 kB\u001b[0m \u001b[31m12.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading fsspec-2024.9.0-py3-none-any.whl (179 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m179.3/179.3 kB\u001b[0m \u001b[31m18.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading multiprocess-0.70.16-py310-none-any.whl (134 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m134.8/134.8 kB\u001b[0m \u001b[31m14.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m179.3/179.3 kB\u001b[0m \u001b[31m17.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading httpx_sse-0.4.0-py3-none-any.whl (7.8 kB)\n", + "Downloading multiprocess-0.70.16-py311-none-any.whl (143 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m143.5/143.5 kB\u001b[0m \u001b[31m14.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading opentelemetry_exporter_otlp_proto_grpc-1.29.0-py3-none-any.whl (18 kB)\n", "Downloading overrides-7.7.0-py3-none-any.whl (17 kB)\n", - "Downloading posthog-3.7.5-py2.py3-none-any.whl (54 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m54.9/54.9 kB\u001b[0m \u001b[31m5.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading starlette-0.41.3-py3-none-any.whl (73 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m73.2/73.2 kB\u001b[0m \u001b[31m7.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "Downloading posthog-3.8.4-py2.py3-none-any.whl (69 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m69.8/69.8 kB\u001b[0m \u001b[31m5.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading pydantic_settings-2.7.1-py3-none-any.whl (29 kB)\n", + "Downloading sse_starlette-2.2.1-py3-none-any.whl (10 kB)\n", + "Downloading starlette-0.41.3-py3-none-any.whl (73 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m73.2/73.2 kB\u001b[0m \u001b[31m7.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading chevron-0.14.0-py3-none-any.whl (11 kB)\n", - "Downloading levenshtein-0.26.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (162 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m162.6/162.6 kB\u001b[0m \u001b[31m16.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (194 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m194.1/194.1 kB\u001b[0m \u001b[31m20.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "Downloading levenshtein-0.26.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (162 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m162.7/162.7 kB\u001b[0m \u001b[31m17.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (194 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m194.8/194.8 kB\u001b[0m \u001b[31m21.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", "\u001b[?25hDownloading backoff-2.2.1-py3-none-any.whl (15 kB)\n", "Downloading monotonic-1.6-py2.py3-none-any.whl (8.2 kB)\n", "Downloading protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl (319 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m319.7/319.7 kB\u001b[0m \u001b[31m26.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hDownloading rapidfuzz-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.1 MB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.1/3.1 MB\u001b[0m \u001b[31m102.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", - "\u001b[?25hInstalling collected packages: monotonic, chevron, xxhash, uvicorn, redis, rapidfuzz, pypdf, psycopg2-binary, protobuf, pillow, overrides, fsspec, faiss-cpu, dill, braintrust_core, backoff, aiosqlite, starlette, posthog, opentelemetry-proto, multiprocess, levenshtein, opentelemetry-exporter-otlp-proto-common, fastapi, together, autoevals, opentelemetry-exporter-otlp-proto-http, opentelemetry-exporter-otlp-proto-grpc, datasets, chromadb-client\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m319.7/319.7 kB\u001b[0m \u001b[31m28.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hDownloading rapidfuzz-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (3.1 MB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.1/3.1 MB\u001b[0m \u001b[31m84.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: monotonic, chevron, xxhash, uvicorn, redis, rapidfuzz, pypdf, psycopg2-binary, protobuf, pillow, overrides, httpx-sse, fsspec, faiss-cpu, dill, braintrust_core, backoff, anyio, aiosqlite, starlette, posthog, opentelemetry-proto, multiprocess, levenshtein, sse-starlette, pydantic-settings, opentelemetry-exporter-otlp-proto-common, fastapi, together, mcp, datasets, autoevals, opentelemetry-exporter-otlp-proto-http, opentelemetry-exporter-otlp-proto-grpc, chromadb-client\n", " Attempting uninstall: protobuf\n", " Found existing installation: protobuf 4.25.5\n", " Uninstalling protobuf-4.25.5:\n", @@ -546,24 +511,41 @@ " Found existing installation: fsspec 2024.10.0\n", " Uninstalling fsspec-2024.10.0:\n", " Successfully uninstalled fsspec-2024.10.0\n", + " Attempting uninstall: anyio\n", + " Found existing installation: anyio 3.7.1\n", + " Uninstalling anyio-3.7.1:\n", + " Successfully uninstalled anyio-3.7.1\n", "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", + "jupyter-server 1.24.0 requires anyio<4,>=3.1.0, but you have anyio 4.8.0 which is incompatible.\n", "gcsfs 2024.10.0 requires fsspec==2024.10.0, but you have fsspec 2024.9.0 which is incompatible.\n", - "tensorflow 2.17.1 requires protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.20.3, but you have protobuf 5.29.3 which is incompatible.\n", - "tensorflow-metadata 1.13.1 requires protobuf<5,>=3.20.3, but you have protobuf 5.29.3 which is incompatible.\u001b[0m\u001b[31m\n", - "\u001b[0mSuccessfully installed aiosqlite-0.20.0 autoevals-0.0.115 backoff-2.2.1 braintrust_core-0.0.57 chevron-0.14.0 chromadb-client-0.6.2 datasets-3.2.0 dill-0.3.8 faiss-cpu-1.9.0.post1 fastapi-0.115.6 fsspec-2024.9.0 levenshtein-0.26.1 monotonic-1.6 multiprocess-0.70.16 opentelemetry-exporter-otlp-proto-common-1.29.0 opentelemetry-exporter-otlp-proto-grpc-1.29.0 opentelemetry-exporter-otlp-proto-http-1.29.0 opentelemetry-proto-1.29.0 overrides-7.7.0 pillow-10.4.0 posthog-3.7.5 protobuf-5.29.3 psycopg2-binary-2.9.10 pypdf-5.1.0 rapidfuzz-3.11.0 redis-5.2.1 starlette-0.41.3 together-1.3.11 uvicorn-0.34.0 xxhash-3.5.0\n", - "sentence-transformers --no-deps\n", - "Requirement already satisfied: sentence-transformers in /usr/local/lib/python3.10/dist-packages (3.3.1)\n", + "tensorflow 2.17.1 requires protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.20.3, but you have protobuf 5.29.3 which is incompatible.\u001b[0m\u001b[31m\n", + "\u001b[0mSuccessfully installed aiosqlite-0.20.0 anyio-4.8.0 autoevals-0.0.117 backoff-2.2.1 braintrust_core-0.0.58 chevron-0.14.0 chromadb-client-0.6.3 datasets-3.2.0 dill-0.3.8 faiss-cpu-1.9.0.post1 fastapi-0.115.6 fsspec-2024.9.0 httpx-sse-0.4.0 levenshtein-0.26.1 mcp-1.2.0 monotonic-1.6 multiprocess-0.70.16 opentelemetry-exporter-otlp-proto-common-1.29.0 opentelemetry-exporter-otlp-proto-grpc-1.29.0 opentelemetry-exporter-otlp-proto-http-1.29.0 opentelemetry-proto-1.29.0 overrides-7.7.0 pillow-10.4.0 posthog-3.8.4 protobuf-5.29.3 psycopg2-binary-2.9.10 pydantic-settings-2.7.1 pypdf-5.1.0 rapidfuzz-3.11.0 redis-5.2.1 sse-starlette-2.2.1 starlette-0.41.3 together-1.3.11 uvicorn-0.34.0 xxhash-3.5.0\n", "torch --index-url https://download.pytorch.org/whl/cpu\n", "Looking in indexes: https://download.pytorch.org/whl/cpu\n", - "Requirement already satisfied: torch in /usr/local/lib/python3.10/dist-packages (2.5.1+cu121)\n", - "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch) (3.16.1)\n", - "Requirement already satisfied: typing-extensions>=4.8.0 in /usr/local/lib/python3.10/dist-packages (from torch) (4.12.2)\n", - "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch) (3.4.2)\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch) (3.1.5)\n", - "Requirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from torch) (2024.9.0)\n", - "Requirement already satisfied: sympy==1.13.1 in /usr/local/lib/python3.10/dist-packages (from torch) (1.13.1)\n", - "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from sympy==1.13.1->torch) (1.3.0)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch) (3.0.2)\n", + "Requirement already satisfied: torch in /usr/local/lib/python3.11/dist-packages (2.5.1+cu121)\n", + "Requirement already satisfied: filelock in /usr/local/lib/python3.11/dist-packages (from torch) (3.16.1)\n", + "Requirement already satisfied: typing-extensions>=4.8.0 in /usr/local/lib/python3.11/dist-packages (from torch) (4.12.2)\n", + "Requirement already satisfied: networkx in /usr/local/lib/python3.11/dist-packages (from torch) (3.4.2)\n", + "Requirement already satisfied: jinja2 in /usr/local/lib/python3.11/dist-packages (from torch) (3.1.5)\n", + "Requirement already satisfied: fsspec in /usr/local/lib/python3.11/dist-packages (from torch) (2024.9.0)\n", + "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /usr/local/lib/python3.11/dist-packages (from torch) (12.1.105)\n", + "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /usr/local/lib/python3.11/dist-packages (from torch) (12.1.105)\n", + "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /usr/local/lib/python3.11/dist-packages (from torch) (12.1.105)\n", + "Requirement already satisfied: nvidia-cudnn-cu12==9.1.0.70 in /usr/local/lib/python3.11/dist-packages (from torch) (9.1.0.70)\n", + "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /usr/local/lib/python3.11/dist-packages (from torch) (12.1.3.1)\n", + "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /usr/local/lib/python3.11/dist-packages (from torch) (11.0.2.54)\n", + "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /usr/local/lib/python3.11/dist-packages (from torch) (10.3.2.106)\n", + "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /usr/local/lib/python3.11/dist-packages (from torch) (11.4.5.107)\n", + "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /usr/local/lib/python3.11/dist-packages (from torch) (12.1.0.106)\n", + "Requirement already satisfied: nvidia-nccl-cu12==2.21.5 in /usr/local/lib/python3.11/dist-packages (from torch) (2.21.5)\n", + "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /usr/local/lib/python3.11/dist-packages (from torch) (12.1.105)\n", + "Requirement already satisfied: triton==3.1.0 in /usr/local/lib/python3.11/dist-packages (from torch) (3.1.0)\n", + "Requirement already satisfied: sympy==1.13.1 in /usr/local/lib/python3.11/dist-packages (from torch) (1.13.1)\n", + "Requirement already satisfied: nvidia-nvjitlink-cu12 in /usr/local/lib/python3.11/dist-packages (from nvidia-cusolver-cu12==11.4.5.107->torch) (12.6.85)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.11/dist-packages (from sympy==1.13.1->torch) (1.3.0)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.11/dist-packages (from jinja2->torch) (3.0.2)\n", + "sentence-transformers --no-deps\n", + "Requirement already satisfied: sentence-transformers in /usr/local/lib/python3.11/dist-packages (3.3.1)\n", "\u001b[32mBuild Successful!\u001b[0m\n" ] } @@ -589,346 +571,330 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "id": "E1UFuJC570Tk", "metadata": { "colab": { "base_uri": "https://localhost:8080/", "height": 1000, "referenced_widgets": [ - "88f0c88612bb45d59f07e93567cc0e14", - "9b24a82117e1482a8f6665978e84089c", - "8e75bf7cac454eeabd5ce47a1e981c68", - "fc272883566541108f83117ccd146a21", - "2e27a025a416434f8ab3b63049626d11", - "3a46a46bc8124a92b27aef43cbc009b6", - "4ad6bc0cca62446d8faf19a341bfa86f", - "6437c99289f947449f7d2964288973e5", - "e2f7dea8fc744537b42d0f1a85a73eb4", - "1377d2160344430da8f29a50d113a288", - "0c0b30e126724f9282ac5acbcb4581db", - "895efd0b6d9f4b319159703d965d1966", - "dece6dff65394a5f93585c73359d4dad", - "1030c0848635497681cc9ff0c344fb1a", - "fa6ecaab432347de8427b9b5ac3d4524", - "5effefa8e3764e3aaff57fe0197a7c96", - "1756eceba2c34c1ca182b7db465e95ce", - "0fd62e56e0bb41a996c04e63381d2a29", - "29badfc2eb0345d38d7cfc6c7f8bb1a8", - "e64cedb4560a43d8a43f36002087ac30", - "45aadb26b382460eb5b6b147509fb75a", - "130f2f5840764e8dbd573cc8a6ea6f5f", - "9ee45247ec144bb3aafe4208f316063f", - "da330e0999cb4c3c91a1cb1026304568", - "ff58a5381fb74cb1b9efc10f5c2738d6", - "18ed62b1d4594ed9a2651fa5df046efc", - "4004cda1d84949f5a380536f8a9d0274", - "54bddcf41c5641b7a56c981aadb62ef1", - "a9a0d8415d9d4e98a3f02ae8ec1053da", - "cceff1126242494bab432205c7ac7345", - "e6e53c439dab4639adc1c3c873602476", - "95db8eab3f964edf99038ad53f41fabc", - "52f1d69c6cd04816b6f34657893ae32b", - "b79a1dfcf2904bcba332569dbf351f34", - "7363b1a9a1b54a57bf15357e897128fd", - "3ac596104cdc4439b3980f7ce66ad080", - "5c9ec25994914acd8e13866b3eb943e1", - "38a958036c6e4155815a8169f1be1e53", - "cf5113a647ce45c4a3a523361aa3b5af", - "da8c20a65ba541bda058614849d5cfe2", - "40e9f20d74374b0e82c653caa0559d04", - "f46cfc9237e64db6be2ec6529b61ec88", - "dc04575da46540d4ad3a708e58f0de6a", - "24c0be775e474517a7be49d187822bd0", - "111184729957441d9d1f3d404bd82757", - "be060f9d7a664c17a80510f447c0bee3", - "228445132e5f4b2ca793f4beeeca4426", - "b96a2e34a2af435b9705550fe564591d", - "1f1cdac013af4559889f15eebac5256a", - "834ae2d249b94be6bbe5349509536a4b", - "509863a58de74b07b813aa83ffa4a507", - "48a5b775a4324da791603b83d61be7d1", - "02b60dad91c7482ba70cf8bb954bc4eb", - "2bfb0fb5506d4285918a9c94af9ab5d1", - "0f699b0f99484a8ba2eb17bb1d621c5a", - "c6f34317390e4f90b16235f2ae84a981", - "3da95c8814f34472a181ce7687f9e15e", - "4d1c2de4c1354ef0b84c54c447141707", - "31ab98e0e375416b83b36a98d4958f57", - "8b9ebe06b4e045a29269128ec97d9f62", - "53a46fe254924e78876db6dd2e1b7123", - "f2ce01983f0a4f12b318e6d29f1dd4a1", - "1b7af9f7204547b8b4a718a780af0ded", - "a4bb5a59d1324585b0a34c9bb2820b7f", - "90c2e0e012a94521b9f5cb24924771d8", - "2563a4677dde47d0a2f7fba5c5dde358", - "5023c2b8cf9846069d116237826fed7f", - "960c2f44166b4ac7910af6512832186f", - "309ea9620a674088a5207206d9a52d54", - "1c86d856083c4ef99976849c7a1c9100", - "5d9bf2102da143c1b9e1483e05add4e5", - "85569eaf3ae3488b808131cd460f6514", - "3015bc3ce98a4221a9dd3be92481435d", - "4d7b0983b97f48b2a333d5b2a4ec50a8", - "e834a64e49534c3586cb77f4ec5eab2d", - "67f82b82ebb74d0fb3c68b9c8c57d690", - "b710cb57f19d4490a740c060e8a83b90", - "713c09d1275a43b0af7c2ae8e126517f", - "b62fe08114f549ea99808e8df95c7cad", - "af722d177320422e97c679b24cb754f6", - "487477e023b64947bf42f83dc6275ef1", - "bcf0d3af3bc0439e97023937852941e9", - "d83a1e1e678e4efd83115f9aee0ffc8d", - "f210583576594e759387fc704695ad09", - "91e103573c034ceda689047c61294b17", - "b9eac61fb55342f4bf9834f321899836", - "a92a7bce961e4291b126fda3c540636b", - "01b3e7803d1946118d27acda0c067da2", - "f097b32928f246de9b01fea6f9b092f7", - "35e10db3906248ffa8ab955d2f53bd75", - "80e884cae6ea42eaa37f028120963355", - "25821e7aef4e481bbdf3b4698ce3c277", - "916190b4615e4c5c9f3e55c0804a3502", - "1f1dc0d20cae46feb372203aea6458a0", - "43feace0290a47c0b06c3a1c08cc70a9", - "9f185162847f4cb2828af81c92116582", - "3a649adc22694036b35bab04ff03d338", - "7daef1502e2a4140ac021b3b3a6aa12d", - "1307ef0325bb433d8a1bcc653c7fb291", - "f01d7a1404a943a08c84adce14a262c7", - "f15cdedf8e7b4a44993644a5ff070e78", - "b7f9a3c97f2043f380bdc1827961c649", - "0b64892a98d14a3b85b128df77d8e7d6", - "8de1cba3a7c0422eb2a21e3f8b2059c7", - "a0639d5360044f97ac5b9374c735ff4b", - "9b11eaf2d50a447384b75eb7f73829eb", - "8ab411217bfd486ca3fb8b885fff4690", - "c80ea8c54211427087712b5500e26edf", - "542aa4a847cf4a66a4b3fc93c241363b", - "8c0d69b735c94b719160d39256c643cc", - "3c868641db934c67a44e1d26e1a17756", - "a72d01788b484bbeb4375aac3ceadf34", - "366add01dc734455a384460c97491215", - "70accb92e645435b8f1e0c48538f7473", - "628848757fcf443e806a8f25013cc2b5", - "ebf411690c844daf89b87c120e3cb67e", - "79b9fb75dc1d486c9fc881a90b6f1060", - "0f3bbf28fbed4e97b660bbf3c66a214a", - "a4b2220ed47f4f85b3f991c92de98964", - "b6a505e6c863409db1b906423f99125a", - "d9560d20106a42ec904e7e315f99ff01" + "75307e3dee604d30aa44713e6e293e64", + "5ce87402a79342af995df41ac3940d55", + "fbbcc19886cc43b38424fbb184162c61", + "29212208db6b432eb4f708cd64258954", + "50dd8994a4cf486ebbec5ffd4322992a", + "f9b768c703494dd198f2978aff4892e8", + "1231b9e4cab34c33a38bee63543f1e75", + "754deb3970604d48a522bc9f021ad945", + "f6ecca7a1a8340fbbe056235a2714fc3", + "ef4f63fe9d8f4683a9d20becb6e4e2cb", + "7508f10c13634e7aa682cfb29c48d9e7", + "26f1430ca7cb4ad5b1b8df1ffdbd32a9", + "7cd2d9c9ea7b4d70902ffaff33033078", + "101288236cff40b8bb9dbad80dbbc7ee", + "d5c9977838a249eeab6ef628279b8155", + "d032d1e7b4b54ba28ac83c1a12b23876", + "321fce57c158432abeae496ae8a947aa", + "3ebe00201bdb4e119e3b74f684a58345", + "0f8bab6b8ed04774b386fe952aae66f1", + "cfcb6e456c354d99be91f161552f3376", + "61bd0d490c0e4c04a331cf9ce6b7d38f", + "7d8653fca29f4df3a7487733ff9db60b", + "943f8fcb66614353a51f32f8344b6122", + "0e695245b97c4bbc85e349fda3dc07b9", + "bb0d168c41f540b8ae42239d3938483a", + "87700a80125348f28c4f249bdf8b0a8d", + "8902c3622da540e496ed5b1524bd01ca", + "90432ec1c24b4607a935c94e130cd68d", + "464147b149824f20afc727751a702fc7", + "67e37a088be64a2ba786ca923b1017dd", + "98786f52ef5345b0b9164b9c1f2b8e18", + "0e1b9910a77d4b7fa69cb8926e6547d7", + "0b276315be4345be83da1e03905c8495", + "e11f8c3891284e07bd2572257afd5e1b", + "ee18d96394994d01b49d5b03b3d9a019", + "844b06df5749441fab6f61656ce581a9", + "e1c6b9a20e074f17aeba976b24e80c65", + "c690da8daa1e4f9ea73bcacdd92e8a6d", + "d0b161ae25c441e8b3caf7a3d88c1b05", + "47cf4b6b835d43388576a2abf4cc54f8", + "03bbebd659e64b5d9c29a73570c34854", + "b68e5097d2504d2cbd7e19aa1aac3a04", + "22a665deff88477b9372c0350c4c572b", + "5e535ed2b83e496ab57b1c80b615ab0c", + "d9de065c7f81443e98ddf066c7b5bd54", + "1e836106837c4ac7a11b36e700c46b64", + "55591e8179084fcfa3a61c8bd8d09dcb", + "de1ef93c41364eda9b4b111231057348", + "23b0b2f4f82c4a21846e91d7cea91da5", + "9e4d0fbb51284a7487c495c7b95a293d", + "b0f8cf1f79e04b5fb47a810f2c81bd7e", + "0c359bc4c94c46acbc9094354a15c33d", + "59d0b59b6c2248508d0601ff13878d33", + "891cb726d45c4fef8f2c74a56df5532b", + "fa39189070334939aea5fa4a7de5ec8b", + "f0e107dd6d54483aa367da0e337a97cd", + "861a00796f55470e85d94733eeee9a5f", + "5459633eb6e94ec391d13fcf67425726", + "b7b7467ece304ffbbd352b9b96a03aad", + "9dece059f1204e29b106fca9e191ddb3", + "e2e49c25d6fc4592b317e94cfabc2e5e", + "76d37a48a73946bab2821f097cf2605f", + "8e81ae00681347cb906b392c3656a64a", + "74bedc38b7da4e8a83b0c892d7aa59b5", + "d1e67c28b4664e8098dce8f5e80b8779", + "abe6cf39b784436993fcbe92221c31a3", + "d021a18ab70b4c7e8aec43932a124c36", + "72e7c092fb054b7ea0dcd2782b5d8a7d", + "8b1ea80221174fae943d5c9f997dfb57", + "f8073d625f80415dbf712cee434f6e3a", + "5f6014ba13fa4a659b9eb1b5f83599a7", + "327ff8f5292d47afbfebd3beea187739", + "988cac4341b646079fc73719f3f88ad7", + "900a4dac08f540dfb35c29f63236a12c", + "1e6009b9b0684b8fbaa379ea96f111ee", + "541b9b4e74614e2cb855bb90f03df538", + "ff256b2275f740ed82bca4f43b4d6fd2", + "3703041a499c426bb427ee008c81cde5", + "4b22bbacb995425fb32a2368f3685a92", + "49a66eeb9ef74de5ab8904fd90eb7558", + "08f9d125018b41c582a0fa1e234315f9", + "736c770230644894b85dbc34bd8f1d52", + "b67cbbf32f844a19b219be612d5038c9", + "774b513d64524ac7823a2cf13efa8d41", + "1e56da93bcf64ff490416d2b66cd3dc0", + "b7e35038ce344110b785753b655130f5", + "5472af91737446f4a4a2d92a3f684a45", + "9fb4368802da4a5a8101ba200d98403a", + "2e713bcc372e48b2a006558db4d1df68", + "1a277abd5ea44253bc6894bef258b52b", + "b3eedd82e7da4ce8b3ded70e49a2afd0", + "6f5c18cb8002471f8b3764effee37324", + "3bebac362b344e8d9103c5011613f1ea", + "670905a55b19458da69f83c8bcd511d1", + "ff54451a48394faaaa9d8cdb690d0718", + "36b5bc19b2d0407f8ab28ff0da2ce12d", + "879e48d9a9e04183903d94ffe98313d2", + "abce503d70594c2ca9afdc47847c125b", + "028e291ee53947bbbbc4bfb68c695f5f", + "a530662719374c95a9bef12e59e28c85", + "bffc0f4b12f141398535990709fd4f2c", + "04804c74e1dd43449d5f758cf5d0ba5e", + "95a506c3007c4525b01ee4e1600d671b", + "a0d6b0caeb2340fe96c8f5569e3d3ae4", + "30798f87a8b848d783fdacd71af5dc04", + "07ce54c75e76488ba4019a20b3707061", + "f023175de68445f98a6b01bb40ccdc6d", + "7389b79a0ff44cd68c7866995d728023", + "8e2b70ffe4eb4974bd6393fcc1292267", + "13eee164dc534424acb9dc9ee37a9465", + "722a7fe16af3422585a20c651345cfa4", + "f5596c1c9c4d42f3bc171961f9582eff", + "85d66e615b5742e78657b1e60c75fc72", + "731c02dc5dd446c3b22765575148e256", + "254ce460ce244c99a5afe39d5d51f6b7", + "4cf1dc345ace4da59f978f661487f975", + "8f30fca71bf24e5ca26e17c2321f893c", + "dd85d37dd1d14c7ea4592f8e11b2d2c8", + "3cb06377e4454f009d6b2aa7aa6ff0a9", + "4502477db4d948e693012364c2dcb370", + "52fe404ec9c14db2a7279b4c154eef3d" ] }, "collapsed": true, "id": "E1UFuJC570Tk", - "outputId": "0000e930-550b-4bf6-ebc6-184e517f930a" + "outputId": "aebb69d4-c167-4de5-eb8a-dd19dd538f63" }, "outputs": [ { - "name": "stdout", "output_type": "stream", + "name": "stdout", "text": [ - "Not in Google Colab environment\n", - "\u001b[33mWarning: `bwrap` is not available. Code interpreter tool will not work correctly.\u001b[0m\n" + "Removed handler StreamHandler from root logger\n" ] }, { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.11/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning: \n", + "The secret `HF_TOKEN` does not exist in your Colab secrets.\n", + "To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session.\n", + "You will be able to reuse this secret in all of your notebooks.\n", + "Please note that authentication is recommended but still optional to access public models or datasets.\n", + " warnings.warn(\n" + ] + }, + { + "output_type": "display_data", "data": { + "text/plain": [ + "modules.json: 0%| | 0.00/349 [00:00Using config together:\n", "

\n" - ], - "text/plain": [ - "Using config \u001b[34mtogether\u001b[0m:\n" ] }, - "metadata": {}, - "output_type": "display_data" + "metadata": {} }, { + "output_type": "display_data", "data": { - "text/html": [ - "
apis:\n",
-              "- agents\n",
-              "- datasetio\n",
-              "- eval\n",
-              "- inference\n",
-              "- memory\n",
-              "- safety\n",
-              "- scoring\n",
-              "- telemetry\n",
-              "- tool_runtime\n",
-              "conda_env: together\n",
-              "datasets: []\n",
-              "container_image: null\n",
-              "eval_tasks: []\n",
-              "image_name: together\n",
-              "memory_banks: []\n",
-              "metadata_store:\n",
-              "  db_path: /Users/dineshyv/.llama/distributions/together/registry.db\n",
-              "  namespace: null\n",
-              "  type: sqlite\n",
-              "models:\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.1-8B-Instruct\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.1-70B-Instruct\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.1-405B-Instruct-FP8\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.2-3B-Instruct\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Llama-3.2-3B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.2-11B-Vision-Instruct\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.2-90B-Vision-Instruct\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.3-70B-Instruct\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Llama-3.3-70B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-Guard-3-8B\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Meta-Llama-Guard-3-8B\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-Guard-3-11B-Vision\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - llm\n",
-              "  provider_id: together\n",
-              "  provider_model_id: meta-llama/Llama-Guard-3-11B-Vision-Turbo\n",
-              "- metadata:\n",
-              "    embedding_dimension: 384\n",
-              "  model_id: all-MiniLM-L6-v2\n",
-              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
-              "  - embedding\n",
-              "  provider_id: sentence-transformers\n",
-              "  provider_model_id: null\n",
-              "providers:\n",
-              "  agents:\n",
-              "  - config:\n",
-              "      persistence_store:\n",
-              "        db_path: /Users/dineshyv/.llama/distributions/together/agents_store.db\n",
-              "        namespace: null\n",
-              "        type: sqlite\n",
-              "    provider_id: meta-reference\n",
-              "    provider_type: inline::meta-reference\n",
-              "  datasetio:\n",
-              "  - config: {}\n",
-              "    provider_id: huggingface\n",
-              "    provider_type: remote::huggingface\n",
-              "  - config: {}\n",
-              "    provider_id: localfs\n",
-              "    provider_type: inline::localfs\n",
-              "  eval:\n",
-              "  - config: {}\n",
-              "    provider_id: meta-reference\n",
-              "    provider_type: inline::meta-reference\n",
-              "  inference:\n",
-              "  - config:\n",
-              "      api_key: '********'\n",
-              "      url: https://api.together.xyz/v1\n",
-              "    provider_id: together\n",
-              "    provider_type: remote::together\n",
-              "  - config: {}\n",
-              "    provider_id: sentence-transformers\n",
-              "    provider_type: inline::sentence-transformers\n",
-              "  memory:\n",
-              "  - config:\n",
-              "      kvstore:\n",
-              "        db_path: /Users/dineshyv/.llama/distributions/together/faiss_store.db\n",
-              "        namespace: null\n",
-              "        type: sqlite\n",
-              "    provider_id: faiss\n",
-              "    provider_type: inline::faiss\n",
-              "  safety:\n",
-              "  - config: {}\n",
-              "    provider_id: llama-guard\n",
-              "    provider_type: inline::llama-guard\n",
-              "  scoring:\n",
-              "  - config: {}\n",
-              "    provider_id: basic\n",
-              "    provider_type: inline::basic\n",
-              "  - config: {}\n",
-              "    provider_id: llm-as-judge\n",
-              "    provider_type: inline::llm-as-judge\n",
-              "  - config:\n",
-              "      openai_api_key: '********'\n",
-              "    provider_id: braintrust\n",
-              "    provider_type: inline::braintrust\n",
-              "  telemetry:\n",
-              "  - config:\n",
-              "      service_name: llama-stack\n",
-              "      sinks: sqlite\n",
-              "      sqlite_db_path: /Users/dineshyv/.llama/distributions/together/trace_store.db\n",
-              "    provider_id: meta-reference\n",
-              "    provider_type: inline::meta-reference\n",
-              "  tool_runtime:\n",
-              "  - config:\n",
-              "      api_key: '********'\n",
-              "      max_results: 3\n",
-              "    provider_id: brave-search\n",
-              "    provider_type: remote::brave-search\n",
-              "  - config:\n",
-              "      api_key: '********'\n",
-              "      max_results: 3\n",
-              "    provider_id: tavily-search\n",
-              "    provider_type: remote::tavily-search\n",
-              "  - config: {}\n",
-              "    provider_id: code-interpreter\n",
-              "    provider_type: inline::code-interpreter\n",
-              "  - config: {}\n",
-              "    provider_id: memory-runtime\n",
-              "    provider_type: inline::memory-runtime\n",
-              "scoring_fns: []\n",
-              "shields:\n",
-              "- params: null\n",
-              "  provider_id: null\n",
-              "  provider_shield_id: null\n",
-              "  shield_id: meta-llama/Llama-Guard-3-8B\n",
-              "tool_groups:\n",
-              "- args: null\n",
-              "  mcp_endpoint: null\n",
-              "  provider_id: tavily-search\n",
-              "  toolgroup_id: builtin::websearch\n",
-              "- args: null\n",
-              "  mcp_endpoint: null\n",
-              "  provider_id: memory-runtime\n",
-              "  toolgroup_id: builtin::memory\n",
-              "- args: null\n",
-              "  mcp_endpoint: null\n",
-              "  provider_id: code-interpreter\n",
-              "  toolgroup_id: builtin::code_interpreter\n",
-              "version: '2'\n",
-              "\n",
-              "
\n" - ], "text/plain": [ "apis:\n", "- agents\n", @@ -940,14 +906,13 @@ "- scoring\n", "- telemetry\n", "- tool_runtime\n", - "conda_env: together\n", - "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "container_image: null\n", + "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "image_name: together\n", "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "metadata_store:\n", - " db_path: \u001b[35m/Users/dineshyv/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", + " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", "models:\n", @@ -1016,7 +981,7 @@ " agents:\n", " - config:\n", " persistence_store:\n", - " db_path: \u001b[35m/Users/dineshyv/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", + " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", " provider_id: meta-reference\n", @@ -1044,7 +1009,7 @@ " memory:\n", " - config:\n", " kvstore:\n", - " db_path: \u001b[35m/Users/dineshyv/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", + " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", " namespace: null\n", " type: sqlite\n", " provider_id: faiss\n", @@ -1068,7 +1033,7 @@ " - config:\n", " service_name: llama-stack\n", " sinks: sqlite\n", - " sqlite_db_path: \u001b[35m/Users/dineshyv/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", + " sqlite_db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", " provider_id: meta-reference\n", " provider_type: inline::meta-reference\n", " tool_runtime:\n", @@ -1088,6 +1053,9 @@ " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", " provider_id: memory-runtime\n", " provider_type: inline::memory-runtime\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: model-context-protocol\n", + " provider_type: remote::model-context-protocol\n", "scoring_fns: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", "shields:\n", "- params: null\n", @@ -1109,10 +1077,193 @@ " toolgroup_id: builtin::code_interpreter\n", "version: \u001b[32m'2'\u001b[0m\n", "\n" + ], + "text/html": [ + "
apis:\n",
+              "- agents\n",
+              "- datasetio\n",
+              "- eval\n",
+              "- inference\n",
+              "- memory\n",
+              "- safety\n",
+              "- scoring\n",
+              "- telemetry\n",
+              "- tool_runtime\n",
+              "container_image: null\n",
+              "datasets: []\n",
+              "eval_tasks: []\n",
+              "image_name: together\n",
+              "memory_banks: []\n",
+              "metadata_store:\n",
+              "  db_path: /root/.llama/distributions/together/registry.db\n",
+              "  namespace: null\n",
+              "  type: sqlite\n",
+              "models:\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-3.1-8B-Instruct\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-3.1-70B-Instruct\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-3.1-405B-Instruct-FP8\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-3.2-3B-Instruct\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Llama-3.2-3B-Instruct-Turbo\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-3.2-11B-Vision-Instruct\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-3.2-90B-Vision-Instruct\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-3.3-70B-Instruct\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Llama-3.3-70B-Instruct-Turbo\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-Guard-3-8B\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Meta-Llama-Guard-3-8B\n",
+              "- metadata: {}\n",
+              "  model_id: meta-llama/Llama-Guard-3-11B-Vision\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - llm\n",
+              "  provider_id: together\n",
+              "  provider_model_id: meta-llama/Llama-Guard-3-11B-Vision-Turbo\n",
+              "- metadata:\n",
+              "    embedding_dimension: 384\n",
+              "  model_id: all-MiniLM-L6-v2\n",
+              "  model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n",
+              "  - embedding\n",
+              "  provider_id: sentence-transformers\n",
+              "  provider_model_id: null\n",
+              "providers:\n",
+              "  agents:\n",
+              "  - config:\n",
+              "      persistence_store:\n",
+              "        db_path: /root/.llama/distributions/together/agents_store.db\n",
+              "        namespace: null\n",
+              "        type: sqlite\n",
+              "    provider_id: meta-reference\n",
+              "    provider_type: inline::meta-reference\n",
+              "  datasetio:\n",
+              "  - config: {}\n",
+              "    provider_id: huggingface\n",
+              "    provider_type: remote::huggingface\n",
+              "  - config: {}\n",
+              "    provider_id: localfs\n",
+              "    provider_type: inline::localfs\n",
+              "  eval:\n",
+              "  - config: {}\n",
+              "    provider_id: meta-reference\n",
+              "    provider_type: inline::meta-reference\n",
+              "  inference:\n",
+              "  - config:\n",
+              "      api_key: '********'\n",
+              "      url: https://api.together.xyz/v1\n",
+              "    provider_id: together\n",
+              "    provider_type: remote::together\n",
+              "  - config: {}\n",
+              "    provider_id: sentence-transformers\n",
+              "    provider_type: inline::sentence-transformers\n",
+              "  memory:\n",
+              "  - config:\n",
+              "      kvstore:\n",
+              "        db_path: /root/.llama/distributions/together/faiss_store.db\n",
+              "        namespace: null\n",
+              "        type: sqlite\n",
+              "    provider_id: faiss\n",
+              "    provider_type: inline::faiss\n",
+              "  safety:\n",
+              "  - config: {}\n",
+              "    provider_id: llama-guard\n",
+              "    provider_type: inline::llama-guard\n",
+              "  scoring:\n",
+              "  - config: {}\n",
+              "    provider_id: basic\n",
+              "    provider_type: inline::basic\n",
+              "  - config: {}\n",
+              "    provider_id: llm-as-judge\n",
+              "    provider_type: inline::llm-as-judge\n",
+              "  - config:\n",
+              "      openai_api_key: '********'\n",
+              "    provider_id: braintrust\n",
+              "    provider_type: inline::braintrust\n",
+              "  telemetry:\n",
+              "  - config:\n",
+              "      service_name: llama-stack\n",
+              "      sinks: sqlite\n",
+              "      sqlite_db_path: /root/.llama/distributions/together/trace_store.db\n",
+              "    provider_id: meta-reference\n",
+              "    provider_type: inline::meta-reference\n",
+              "  tool_runtime:\n",
+              "  - config:\n",
+              "      api_key: '********'\n",
+              "      max_results: 3\n",
+              "    provider_id: brave-search\n",
+              "    provider_type: remote::brave-search\n",
+              "  - config:\n",
+              "      api_key: '********'\n",
+              "      max_results: 3\n",
+              "    provider_id: tavily-search\n",
+              "    provider_type: remote::tavily-search\n",
+              "  - config: {}\n",
+              "    provider_id: code-interpreter\n",
+              "    provider_type: inline::code-interpreter\n",
+              "  - config: {}\n",
+              "    provider_id: memory-runtime\n",
+              "    provider_type: inline::memory-runtime\n",
+              "  - config: {}\n",
+              "    provider_id: model-context-protocol\n",
+              "    provider_type: remote::model-context-protocol\n",
+              "scoring_fns: []\n",
+              "shields:\n",
+              "- params: null\n",
+              "  provider_id: null\n",
+              "  provider_shield_id: null\n",
+              "  shield_id: meta-llama/Llama-Guard-3-8B\n",
+              "tool_groups:\n",
+              "- args: null\n",
+              "  mcp_endpoint: null\n",
+              "  provider_id: tavily-search\n",
+              "  toolgroup_id: builtin::websearch\n",
+              "- args: null\n",
+              "  mcp_endpoint: null\n",
+              "  provider_id: memory-runtime\n",
+              "  toolgroup_id: builtin::memory\n",
+              "- args: null\n",
+              "  mcp_endpoint: null\n",
+              "  provider_id: code-interpreter\n",
+              "  toolgroup_id: builtin::code_interpreter\n",
+              "version: '2'\n",
+              "\n",
+              "
\n" ] }, - "metadata": {}, - "output_type": "display_data" + "metadata": {} } ], "source": [ @@ -1155,7 +1306,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 4, "id": "ruO9jQna_t_S", "metadata": { "colab": { @@ -1163,24 +1314,24 @@ }, "collapsed": true, "id": "ruO9jQna_t_S", - "outputId": "52edefba-301c-43d6-f3e2-6be8086dc7f5" + "outputId": "ab1722a7-62ab-43bb-9cab-4e45bf62068a" }, "outputs": [ { - "name": "stdout", "output_type": "stream", + "name": "stdout", "text": [ "Available models:\n", - "all-MiniLM-L6-v2 (provider's alias: all-MiniLM-L6-v2) \n", - "meta-llama/Llama-3.1-405B-Instruct-FP8 (provider's alias: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo) \n", - "meta-llama/Llama-3.1-70B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo) \n", "meta-llama/Llama-3.1-8B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo) \n", - "meta-llama/Llama-3.2-11B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo) \n", + "meta-llama/Llama-3.1-70B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo) \n", + "meta-llama/Llama-3.1-405B-Instruct-FP8 (provider's alias: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo) \n", "meta-llama/Llama-3.2-3B-Instruct (provider's alias: meta-llama/Llama-3.2-3B-Instruct-Turbo) \n", + "meta-llama/Llama-3.2-11B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo) \n", "meta-llama/Llama-3.2-90B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo) \n", "meta-llama/Llama-3.3-70B-Instruct (provider's alias: meta-llama/Llama-3.3-70B-Instruct-Turbo) \n", - "meta-llama/Llama-Guard-3-11B-Vision (provider's alias: meta-llama/Llama-Guard-3-11B-Vision-Turbo) \n", "meta-llama/Llama-Guard-3-8B (provider's alias: meta-llama/Meta-Llama-Guard-3-8B) \n", + "meta-llama/Llama-Guard-3-11B-Vision (provider's alias: meta-llama/Llama-Guard-3-11B-Vision-Turbo) \n", + "all-MiniLM-L6-v2 (provider's alias: all-MiniLM-L6-v2) \n", "----\n", "Available shields (safety models):\n", "meta-llama/Llama-Guard-3-8B\n", @@ -1216,7 +1367,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "id": "LINBvv8lwTJh", "metadata": { "colab": { @@ -1224,18 +1375,21 @@ "height": 35 }, "id": "LINBvv8lwTJh", - "outputId": "5b1fe71f-51cf-4633-92a6-277c3cb5bf59" + "outputId": "8b79cb3b-d690-472f-aad1-2ea8553de701" }, "outputs": [ { + "output_type": "execute_result", "data": { "text/plain": [ "'meta-llama/Llama-3.1-70B-Instruct'" - ] + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } }, - "execution_count": 4, "metadata": {}, - "output_type": "execute_result" + "execution_count": 5 } ], "source": [ @@ -1258,19 +1412,19 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "id": "77c29dba", "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "id": "77c29dba", - "outputId": "cc2e8f7e-1164-49be-d432-0a24e763fa83" + "outputId": "4857974f-4c70-4bc4-f90a-6ae49dc9c41e" }, "outputs": [ { - "name": "stdout", "output_type": "stream", + "name": "stdout", "text": [ "Here's a two-sentence poem about a llama:\n", "\n", @@ -1307,7 +1461,9 @@ "cell_type": "code", "execution_count": null, "id": "3fdf9df6", - "metadata": {}, + "metadata": { + "id": "3fdf9df6" + }, "outputs": [], "source": [ "from termcolor import cprint\n", @@ -1349,15 +1505,17 @@ { "cell_type": "markdown", "id": "72e5111e", - "metadata": {}, + "metadata": { + "id": "72e5111e" + }, "source": [ - "Here is an example for you to try a conversation yourself. \n", + "Here is an example for you to try a conversation yourself.\n", "Remember to type `quit` or `exit` after you are done chatting." ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "id": "9496f75c", "metadata": { "colab": { @@ -1434,7 +1592,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "id": "d119026e", "metadata": { "colab": { @@ -1500,7 +1658,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "id": "axdQIRaJCYAV", "metadata": { "colab": { @@ -1590,7 +1748,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "sUJKJxvAFCaI", "metadata": { "colab": { @@ -1769,7 +1927,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "id": "MpMXiMCv97X5", "metadata": { "colab": { @@ -1886,7 +2044,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "id": "WS8Gu5b0APHs", "metadata": { "colab": { @@ -1963,7 +2121,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "GvLWltzZCNkg", "metadata": { "colab": { @@ -2024,7 +2182,12 @@ "269b1ad9dc7b4ebb94d7364c75f3f324", "2256ddab0ae1408abb10ba211a08f794", "42335bcbc6ee40a79d36c5159cc7da06", - "cf694e1b797246b096ae588973dc985f" + "cf694e1b797246b096ae588973dc985f", + "3e764c00c08942caa2ccb6b92ee60a4e", + "af6680f2e60e476d8487aea98a23b84e", + "c26a9d456e904b2b900bf5e0a5964a0d", + "5a3e0b5ae83143329de6507f9bcf83e0", + "3c9bc5588765436da4f1fee2d893cafd" ] }, "id": "GvLWltzZCNkg", @@ -2199,7 +2362,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "GvVRuhO-GOov", "metadata": { "colab": { @@ -2339,7 +2502,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "id": "JqBBVLKdIHHq", "metadata": { "colab": { @@ -2382,6 +2545,869 @@ "plt.show()" ] }, + { + "cell_type": "markdown", + "source": [ + "### 2.5. Using Model Context Protocol\n", + "\n", + "In this example, we will show how tools hosted in an MCP server can be configured to be used by the model.\n", + "\n", + "In the following steps, we will use the [filesystem tool](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) to explore the files and folders available in the /content directory\n", + "\n", + "Use xterm module to start a shell to run the MCP server using the `supergateway` tool which can start an MCP tool and serve it over HTTP." + ], + "metadata": { + "id": "jSfjNN9fMxtm" + }, + "id": "jSfjNN9fMxtm" + }, + { + "cell_type": "code", + "source": [ + "!pip install colab-xterm #https://pypi.org/project/colab-xterm/\n", + "%load_ext colabxterm" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "67fDKVVpNuFb", + "outputId": "aec2e3cf-e1c3-4d09-d9dc-c4a2f1327e99" + }, + "id": "67fDKVVpNuFb", + "execution_count": 8, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Collecting colab-xterm\n", + " Downloading colab_xterm-0.2.0-py3-none-any.whl.metadata (1.2 kB)\n", + "Requirement already satisfied: ptyprocess~=0.7.0 in /usr/local/lib/python3.11/dist-packages (from colab-xterm) (0.7.0)\n", + "Requirement already satisfied: tornado>5.1 in /usr/local/lib/python3.11/dist-packages (from colab-xterm) (6.3.3)\n", + "Downloading colab_xterm-0.2.0-py3-none-any.whl (115 kB)\n", + "\u001b[?25l \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m0.0/115.6 kB\u001b[0m \u001b[31m?\u001b[0m eta \u001b[36m-:--:--\u001b[0m\r\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m115.6/115.6 kB\u001b[0m \u001b[31m4.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: colab-xterm\n", + "Successfully installed colab-xterm-0.2.0\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "\n", + "%xterm\n", + "# touch /content/foo\n", + "# touch /content/bar\n", + "# npx -y supergateway --port 8000 --stdio 'npx -y @modelcontextprotocol/server-filesystem /content'" + ], + "metadata": { + "colab": { + "resources": { + "https://localhost:10000/": { + "data": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGhlYWQ+PG1ldGEgY2hhcnNldD0idXRmLTgiLz48c2NyaXB0IGRlZmVyPSJkZWZlciIgc3JjPSJtYWluLmpzIj48L3NjcmlwdD48L2hlYWQ+PGJvZHk+PGRpdiBpZD0idGVybWluYWwiPjwvZGl2PjwvYm9keT48L2h0bWw+", + "ok": true, + "headers": [ + [ + "content-length", + "147" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/main.js": { + "data": "LyohIEZvciBsaWNlbnNlIGluZm9ybWF0aW9uIHBsZWFzZSBzZWUgbWFpbi5qcy5MSUNFTlNFLnR4dCAqLwooKCk9Pnt2YXIgZT17MTAyOihlLHQscik9PnsidXNlIHN0cmljdCI7ci5kKHQse1o6KCk9PmF9KTt2YXIgaT1yKDgxKSxuPXIubihpKSxvPXIoNjQ1KSxzPXIubihvKSgpKG4oKSk7cy5wdXNoKFtlLmlkLCcvKipcbiAqIENvcHlyaWdodCAoYykgMjAxNCBUaGUgeHRlcm0uanMgYXV0aG9ycy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqIENvcHlyaWdodCAoYykgMjAxMi0yMDEzLCBDaHJpc3RvcGhlciBKZWZmcmV5IChNSVQgTGljZW5zZSlcbiAqIGh0dHBzOi8vZ2l0aHViLmNvbS9jaGpqL3Rlcm0uanNcbiAqIEBsaWNlbnNlIE1JVFxuICpcbiAqIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHlcbiAqIG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlICJTb2Z0d2FyZSIpLCB0byBkZWFsXG4gKiBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzXG4gKiB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsXG4gKiBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXNcbiAqIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6XG4gKlxuICogVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW5cbiAqIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuICpcbiAqIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCAiQVMgSVMiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SXG4gKiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbiAqIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRVxuICogQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUlxuICogTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbiAqIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU5cbiAqIFRIRSBTT0ZUV0FSRS5cbiAqXG4gKiBPcmlnaW5hbGx5IGZvcmtlZCBmcm9tICh3aXRoIHRoZSBhdXRob3JcJ3MgcGVybWlzc2lvbik6XG4gKiAgIEZhYnJpY2UgQmVsbGFyZFwncyBqYXZhc2NyaXB0IHZ0MTAwIGZvciBqc2xpbnV4OlxuICogICBodHRwOi8vYmVsbGFyZC5vcmcvanNsaW51eC9cbiAqICAgQ29weXJpZ2h0IChjKSAyMDExIEZhYnJpY2UgQmVsbGFyZFxuICogICBUaGUgb3JpZ2luYWwgZGVzaWduIHJlbWFpbnMuIFRoZSB0ZXJtaW5hbCBpdHNlbGZcbiAqICAgaGFzIGJlZW4gZXh0ZW5kZWQgdG8gaW5jbHVkZSB4dGVybSBDU0kgY29kZXMsIGFtb25nXG4gKiAgIG90aGVyIGZlYXR1cmVzLlxuICovXG5cbi8qKlxuICogIERlZmF1bHQgc3R5bGVzIGZvciB4dGVybS5qc1xuICovXG5cbi54dGVybSB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIC1tb3otdXNlci1zZWxlY3Q6IG5vbmU7XG4gICAgICAgICB1c2VyLXNlbGVjdDogbm9uZTtcbiAgICAtbXMtdXNlci1zZWxlY3Q6IG5vbmU7XG4gICAgLXdlYmtpdC11c2VyLXNlbGVjdDogbm9uZTtcbn1cblxuLnh0ZXJtLmZvY3VzLFxuLnh0ZXJtOmZvY3VzIHtcbiAgICBvdXRsaW5lOiBub25lO1xufVxuXG4ueHRlcm0gLnh0ZXJtLWhlbHBlcnMge1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICB0b3A6IDA7XG4gICAgLyoqXG4gICAgICogVGhlIHotaW5kZXggb2YgdGhlIGhlbHBlcnMgbXVzdCBiZSBoaWdoZXIgdGhhbiB0aGUgY2FudmFzZXMgaW4gb3JkZXIgZm9yXG4gICAgICogSU1FcyB0byBhcHBlYXIgb24gdG9wLlxuICAgICAqL1xuICAgIHotaW5kZXg6IDU7XG59XG5cbi54dGVybSAueHRlcm0taGVscGVyLXRleHRhcmVhIHtcbiAgICBwYWRkaW5nOiAwO1xuICAgIGJvcmRlcjogMDtcbiAgICBtYXJnaW46IDA7XG4gICAgLyogTW92ZSB0ZXh0YXJlYSBvdXQgb2YgdGhlIHNjcmVlbiB0byB0aGUgZmFyIGxlZnQsIHNvIHRoYXQgdGhlIGN1cnNvciBpcyBub3QgdmlzaWJsZSAqL1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICBvcGFjaXR5OiAwO1xuICAgIGxlZnQ6IC05OTk5ZW07XG4gICAgdG9wOiAwO1xuICAgIHdpZHRoOiAwO1xuICAgIGhlaWdodDogMDtcbiAgICB6LWluZGV4OiAtNTtcbiAgICAvKiogUHJldmVudCB3cmFwcGluZyBzbyB0aGUgSU1FIGFwcGVhcnMgYWdhaW5zdCB0aGUgdGV4dGFyZWEgYXQgdGhlIGNvcnJlY3QgcG9zaXRpb24gKi9cbiAgICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgcmVzaXplOiBub25lO1xufVxuXG4ueHRlcm0gLmNvbXBvc2l0aW9uLXZpZXcge1xuICAgIC8qIFRPRE86IENvbXBvc2l0aW9uIHBvc2l0aW9uIGdvdCBtZXNzZWQgdXAgc29tZXdoZXJlICovXG4gICAgYmFja2dyb3VuZDogIzAwMDtcbiAgICBjb2xvcjogI0ZGRjtcbiAgICBkaXNwbGF5OiBub25lO1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICAgIHotaW5kZXg6IDE7XG59XG5cbi54dGVybSAuY29tcG9zaXRpb24tdmlldy5hY3RpdmUge1xuICAgIGRpc3BsYXk6IGJsb2NrO1xufVxuXG4ueHRlcm0gLnh0ZXJtLXZpZXdwb3J0IHtcbiAgICAvKiBPbiBPUyBYIHRoaXMgaXMgcmVxdWlyZWQgaW4gb3JkZXIgZm9yIHRoZSBzY3JvbGwgYmFyIHRvIGFwcGVhciBmdWxseSBvcGFxdWUgKi9cbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwO1xuICAgIG92ZXJmbG93LXk6IHNjcm9sbDtcbiAgICBjdXJzb3I6IGRlZmF1bHQ7XG4gICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgIHJpZ2h0OiAwO1xuICAgIGxlZnQ6IDA7XG4gICAgdG9wOiAwO1xuICAgIGJvdHRvbTogMDtcbn1cblxuLnh0ZXJtIC54dGVybS1zY3JlZW4ge1xuICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbn1cblxuLnh0ZXJtIC54dGVybS1zY3JlZW4gY2FudmFzIHtcbiAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gICAgbGVmdDogMDtcbiAgICB0b3A6IDA7XG59XG5cbi54dGVybSAueHRlcm0tc2Nyb2xsLWFyZWEge1xuICAgIHZpc2liaWxpdHk6IGhpZGRlbjtcbn1cblxuLnh0ZXJtLWNoYXItbWVhc3VyZS1lbGVtZW50IHtcbiAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gICAgdmlzaWJpbGl0eTogaGlkZGVuO1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICB0b3A6IDA7XG4gICAgbGVmdDogLTk5OTllbTtcbiAgICBsaW5lLWhlaWdodDogbm9ybWFsO1xufVxuXG4ueHRlcm0ge1xuICAgIGN1cnNvcjogdGV4dDtcbn1cblxuLnh0ZXJtLmVuYWJsZS1tb3VzZS1ldmVudHMge1xuICAgIC8qIFdoZW4gbW91c2UgZXZlbnRzIGFyZSBlbmFibGVkIChlZy4gdG11eCksIHJldmVydCB0byB0aGUgc3RhbmRhcmQgcG9pbnRlciBjdXJzb3IgKi9cbiAgICBjdXJzb3I6IGRlZmF1bHQ7XG59XG5cbi54dGVybS54dGVybS1jdXJzb3ItcG9pbnRlcixcbi54dGVybSAueHRlcm0tY3Vyc29yLXBvaW50ZXIge1xuICAgIGN1cnNvcjogcG9pbnRlcjtcbn1cblxuLnh0ZXJtLmNvbHVtbi1zZWxlY3QuZm9jdXMge1xuICAgIC8qIENvbHVtbiBzZWxlY3Rpb24gbW9kZSAqL1xuICAgIGN1cnNvcjogY3Jvc3NoYWlyO1xufVxuXG4ueHRlcm0gLnh0ZXJtLWFjY2Vzc2liaWxpdHksXG4ueHRlcm0gLnh0ZXJtLW1lc3NhZ2Uge1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICBsZWZ0OiAwO1xuICAgIHRvcDogMDtcbiAgICBib3R0b206IDA7XG4gICAgcmlnaHQ6IDA7XG4gICAgei1pbmRleDogMTA7XG4gICAgY29sb3I6IHRyYW5zcGFyZW50O1xufVxuXG4ueHRlcm0gLmxpdmUtcmVnaW9uIHtcbiAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gICAgbGVmdDogLTk5OTlweDtcbiAgICB3aWR0aDogMXB4O1xuICAgIGhlaWdodDogMXB4O1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG59XG5cbi54dGVybS1kaW0ge1xuICAgIG9wYWNpdHk6IDAuNTtcbn1cblxuLnh0ZXJtLXVuZGVybGluZSB7XG4gICAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7XG59XG5cbi54dGVybS1zdHJpa2V0aHJvdWdoIHtcbiAgICB0ZXh0LWRlY29yYXRpb246IGxpbmUtdGhyb3VnaDtcbn1cbicsIiJdKTtjb25zdCBhPXN9LDY0NTplPT57InVzZSBzdHJpY3QiO2UuZXhwb3J0cz1mdW5jdGlvbihlKXt2YXIgdD1bXTtyZXR1cm4gdC50b1N0cmluZz1mdW5jdGlvbigpe3JldHVybiB0aGlzLm1hcCgoZnVuY3Rpb24odCl7dmFyIHI9IiIsaT12b2lkIDAhPT10WzVdO3JldHVybiB0WzRdJiYocis9IkBzdXBwb3J0cyAoIi5jb25jYXQodFs0XSwiKSB7IikpLHRbMl0mJihyKz0iQG1lZGlhICIuY29uY2F0KHRbMl0sIiB7IikpLGkmJihyKz0iQGxheWVyIi5jb25jYXQodFs1XS5sZW5ndGg+MD8iICIuY29uY2F0KHRbNV0pOiIiLCIgeyIpKSxyKz1lKHQpLGkmJihyKz0ifSIpLHRbMl0mJihyKz0ifSIpLHRbNF0mJihyKz0ifSIpLHJ9KSkuam9pbigiIil9LHQuaT1mdW5jdGlvbihlLHIsaSxuLG8peyJzdHJpbmciPT10eXBlb2YgZSYmKGU9W1tudWxsLGUsdm9pZCAwXV0pO3ZhciBzPXt9O2lmKGkpZm9yKHZhciBhPTA7YTx0aGlzLmxlbmd0aDthKyspe3ZhciBjPXRoaXNbYV1bMF07bnVsbCE9YyYmKHNbY109ITApfWZvcih2YXIgbD0wO2w8ZS5sZW5ndGg7bCsrKXt2YXIgdT1bXS5jb25jYXQoZVtsXSk7aSYmc1t1WzBdXXx8KHZvaWQgMCE9PW8mJih2b2lkIDA9PT11WzVdfHwodVsxXT0iQGxheWVyIi5jb25jYXQodVs1XS5sZW5ndGg+MD8iICIuY29uY2F0KHVbNV0pOiIiLCIgeyIpLmNvbmNhdCh1WzFdLCJ9IikpLHVbNV09byksciYmKHVbMl0/KHVbMV09IkBtZWRpYSAiLmNvbmNhdCh1WzJdLCIgeyIpLmNvbmNhdCh1WzFdLCJ9IiksdVsyXT1yKTp1WzJdPXIpLG4mJih1WzRdPyh1WzFdPSJAc3VwcG9ydHMgKCIuY29uY2F0KHVbNF0sIikgeyIpLmNvbmNhdCh1WzFdLCJ9IiksdVs0XT1uKTp1WzRdPSIiLmNvbmNhdChuKSksdC5wdXNoKHUpKX19LHR9fSw4MTplPT57InVzZSBzdHJpY3QiO2UuZXhwb3J0cz1mdW5jdGlvbihlKXtyZXR1cm4gZVsxXX19LDQ4NjpmdW5jdGlvbihlLHQscil7dmFyIGk7ZT1yLm5tZChlKSxmdW5jdGlvbigpe3ZhciBuLG89IkV4cGVjdGVkIGEgZnVuY3Rpb24iLHM9Il9fbG9kYXNoX2hhc2hfdW5kZWZpbmVkX18iLGE9Il9fbG9kYXNoX3BsYWNlaG9sZGVyX18iLGM9MzIsbD0xMjgsdT0xLzAsaD05MDA3MTk5MjU0NzQwOTkxLGY9TmFOLF89NDI5NDk2NzI5NSxkPVtbImFyeSIsbF0sWyJiaW5kIiwxXSxbImJpbmRLZXkiLDJdLFsiY3VycnkiLDhdLFsiY3VycnlSaWdodCIsMTZdLFsiZmxpcCIsNTEyXSxbInBhcnRpYWwiLGNdLFsicGFydGlhbFJpZ2h0Iiw2NF0sWyJyZWFyZyIsMjU2XV0scD0iW29iamVjdCBBcmd1bWVudHNdIix2PSJbb2JqZWN0IEFycmF5XSIsZz0iW29iamVjdCBCb29sZWFuXSIseT0iW29iamVjdCBEYXRlXSIsbT0iW29iamVjdCBFcnJvcl0iLGI9IltvYmplY3QgRnVuY3Rpb25dIixTPSJbb2JqZWN0IEdlbmVyYXRvckZ1bmN0aW9uXSIsQz0iW29iamVjdCBNYXBdIix3PSJbb2JqZWN0IE51bWJlcl0iLEw9IltvYmplY3QgT2JqZWN0XSIsRT0iW29iamVjdCBQcm9taXNlXSIseD0iW29iamVjdCBSZWdFeHBdIixBPSJbb2JqZWN0IFNldF0iLGs9IltvYmplY3QgU3RyaW5nXSIsTT0iW29iamVjdCBTeW1ib2xdIixSPSJbb2JqZWN0IFdlYWtNYXBdIixUPSJbb2JqZWN0IEFycmF5QnVmZmVyXSIsTz0iW29iamVjdCBEYXRhVmlld10iLEI9IltvYmplY3QgRmxvYXQzMkFycmF5XSIsRD0iW29iamVjdCBGbG9hdDY0QXJyYXldIixQPSJbb2JqZWN0IEludDhBcnJheV0iLEk9IltvYmplY3QgSW50MTZBcnJheV0iLEg9IltvYmplY3QgSW50MzJBcnJheV0iLGo9IltvYmplY3QgVWludDhBcnJheV0iLEY9IltvYmplY3QgVWludDhDbGFtcGVkQXJyYXldIixXPSJbb2JqZWN0IFVpbnQxNkFycmF5XSIsVT0iW29iamVjdCBVaW50MzJBcnJheV0iLHE9L1xiX19wIFwrPSAnJzsvZyxOPS9cYihfX3AgXCs9KSAnJyBcKy9nLHo9LyhfX2VcKC4qP1wpfFxiX190XCkpIFwrXG4nJzsvZyxLPS8mKD86YW1wfGx0fGd0fHF1b3R8IzM5KTsvZyxWPS9bJjw+IiddL2csRz1SZWdFeHAoSy5zb3VyY2UpLFk9UmVnRXhwKFYuc291cmNlKSxYPS88JS0oW1xzXFNdKz8pJT4vZyxaPS88JShbXHNcU10rPyklPi9nLEo9LzwlPShbXHNcU10rPyklPi9nLCQ9L1wufFxbKD86W15bXF1dKnwoWyInXSkoPzooPyFcMSlbXlxcXXxcXC4pKj9cMSlcXS8sUT0vXlx3KiQvLGVlPS9bXi5bXF1dK3xcWyg/OigtP1xkKyg/OlwuXGQrKT8pfChbIiddKSgoPzooPyFcMilbXlxcXXxcXC4pKj8pXDIpXF18KD89KD86XC58XFtcXSkoPzpcLnxcW1xdfCQpKS9nLHRlPS9bXFxeJC4qKz8oKVtcXXt9fF0vZyxyZT1SZWdFeHAodGUuc291cmNlKSxpZT0vXlxzKy8sbmU9L1xzLyxvZT0vXHsoPzpcblwvXCogXFt3cmFwcGVkIHdpdGggLitcXSBcKlwvKT9cbj8vLHNlPS9ce1xuXC9cKiBcW3dyYXBwZWQgd2l0aCAoLispXF0gXCovLGFlPS8sPyAmIC8sY2U9L1teXHgwMC1ceDJmXHgzYS1ceDQwXHg1Yi1ceDYwXHg3Yi1ceDdmXSsvZyxsZT0vWygpPSx7fVxbXF1cL1xzXS8sdWU9L1xcKFxcKT8vZyxoZT0vXCRceyhbXlxcfV0qKD86XFwuW15cXH1dKikqKVx9L2csZmU9L1x3KiQvLF9lPS9eWy0rXTB4WzAtOWEtZl0rJC9pLGRlPS9eMGJbMDFdKyQvaSxwZT0vXlxbb2JqZWN0IC4rP0NvbnN0cnVjdG9yXF0kLyx2ZT0vXjBvWzAtN10rJC9pLGdlPS9eKD86MHxbMS05XVxkKikkLyx5ZT0vW1x4YzAtXHhkNlx4ZDgtXHhmNlx4ZjgtXHhmZlx1MDEwMC1cdTAxN2ZdL2csbWU9LygkXikvLGJlPS9bJ1xuXHJcdTIwMjhcdTIwMjlcXF0vZyxTZT0iXFx1MDMwMC1cXHUwMzZmXFx1ZmUyMC1cXHVmZTJmXFx1MjBkMC1cXHUyMGZmIixDZT0iYS16XFx4ZGYtXFx4ZjZcXHhmOC1cXHhmZiIsd2U9IkEtWlxceGMwLVxceGQ2XFx4ZDgtXFx4ZGUiLExlPSJcXHhhY1xceGIxXFx4ZDdcXHhmN1xceDAwLVxceDJmXFx4M2EtXFx4NDBcXHg1Yi1cXHg2MFxceDdiLVxceGJmXFx1MjAwMC1cXHUyMDZmIFxcdFxceDBiXFxmXFx4YTBcXHVmZWZmXFxuXFxyXFx1MjAyOFxcdTIwMjlcXHUxNjgwXFx1MTgwZVxcdTIwMDBcXHUyMDAxXFx1MjAwMlxcdTIwMDNcXHUyMDA0XFx1MjAwNVxcdTIwMDZcXHUyMDA3XFx1MjAwOFxcdTIwMDlcXHUyMDBhXFx1MjAyZlxcdTIwNWZcXHUzMDAwIixFZT0iWyIrTGUrIl0iLHhlPSJbIitTZSsiXSIsQWU9IlxcZCsiLGtlPSJbIitDZSsiXSIsTWU9IlteXFx1ZDgwMC1cXHVkZmZmIitMZStBZSsiXFx1MjcwMC1cXHUyN2JmIitDZSt3ZSsiXSIsUmU9IlxcdWQ4M2NbXFx1ZGZmYi1cXHVkZmZmXSIsVGU9IlteXFx1ZDgwMC1cXHVkZmZmXSIsT2U9Iig/OlxcdWQ4M2NbXFx1ZGRlNi1cXHVkZGZmXSl7Mn0iLEJlPSJbXFx1ZDgwMC1cXHVkYmZmXVtcXHVkYzAwLVxcdWRmZmZdIixEZT0iWyIrd2UrIl0iLFBlPSIoPzoiK2tlKyJ8IitNZSsiKSIsSWU9Iig/OiIrRGUrInwiK01lKyIpIixIZT0iKD86WyfigJldKD86ZHxsbHxtfHJlfHN8dHx2ZSkpPyIsamU9Iig/Olsn4oCZXSg/OkR8TEx8TXxSRXxTfFR8VkUpKT8iLEZlPSIoPzoiK3hlKyJ8IitSZSsiKT8iLFdlPSJbXFx1ZmUwZVxcdWZlMGZdPyIsVWU9V2UrRmUrIig/OlxcdTIwMGQoPzoiK1tUZSxPZSxCZV0uam9pbigifCIpKyIpIitXZStGZSsiKSoiLHFlPSIoPzoiK1siW1xcdTI3MDAtXFx1MjdiZl0iLE9lLEJlXS5qb2luKCJ8IikrIikiK1VlLE5lPSIoPzoiK1tUZSt4ZSsiPyIseGUsT2UsQmUsIltcXHVkODAwLVxcdWRmZmZdIl0uam9pbigifCIpKyIpIix6ZT1SZWdFeHAoIlsn4oCZXSIsImciKSxLZT1SZWdFeHAoeGUsImciKSxWZT1SZWdFeHAoUmUrIig/PSIrUmUrIil8IitOZStVZSwiZyIpLEdlPVJlZ0V4cChbRGUrIj8iK2tlKyIrIitIZSsiKD89IitbRWUsRGUsIiQiXS5qb2luKCJ8IikrIikiLEllKyIrIitqZSsiKD89IitbRWUsRGUrUGUsIiQiXS5qb2luKCJ8IikrIikiLERlKyI/IitQZSsiKyIrSGUsRGUrIisiK2plLCJcXGQqKD86MVNUfDJORHwzUkR8KD8hWzEyM10pXFxkVEgpKD89XFxifFthLXpfXSkiLCJcXGQqKD86MXN0fDJuZHwzcmR8KD8hWzEyM10pXFxkdGgpKD89XFxifFtBLVpfXSkiLEFlLHFlXS5qb2luKCJ8IiksImciKSxZZT1SZWdFeHAoIltcXHUyMDBkXFx1ZDgwMC1cXHVkZmZmIitTZSsiXFx1ZmUwZVxcdWZlMGZdIiksWGU9L1thLXpdW0EtWl18W0EtWl17Mn1bYS16XXxbMC05XVthLXpBLVpdfFthLXpBLVpdWzAtOV18W15hLXpBLVowLTkgXS8sWmU9WyJBcnJheSIsIkJ1ZmZlciIsIkRhdGFWaWV3IiwiRGF0ZSIsIkVycm9yIiwiRmxvYXQzMkFycmF5IiwiRmxvYXQ2NEFycmF5IiwiRnVuY3Rpb24iLCJJbnQ4QXJyYXkiLCJJbnQxNkFycmF5IiwiSW50MzJBcnJheSIsIk1hcCIsIk1hdGgiLCJPYmplY3QiLCJQcm9taXNlIiwiUmVnRXhwIiwiU2V0IiwiU3RyaW5nIiwiU3ltYm9sIiwiVHlwZUVycm9yIiwiVWludDhBcnJheSIsIlVpbnQ4Q2xhbXBlZEFycmF5IiwiVWludDE2QXJyYXkiLCJVaW50MzJBcnJheSIsIldlYWtNYXAiLCJfIiwiY2xlYXJUaW1lb3V0IiwiaXNGaW5pdGUiLCJwYXJzZUludCIsInNldFRpbWVvdXQiXSxKZT0tMSwkZT17fTskZVtCXT0kZVtEXT0kZVtQXT0kZVtJXT0kZVtIXT0kZVtqXT0kZVtGXT0kZVtXXT0kZVtVXT0hMCwkZVtwXT0kZVt2XT0kZVtUXT0kZVtnXT0kZVtPXT0kZVt5XT0kZVttXT0kZVtiXT0kZVtDXT0kZVt3XT0kZVtMXT0kZVt4XT0kZVtBXT0kZVtrXT0kZVtSXT0hMTt2YXIgUWU9e307UWVbcF09UWVbdl09UWVbVF09UWVbT109UWVbZ109UWVbeV09UWVbQl09UWVbRF09UWVbUF09UWVbSV09UWVbSF09UWVbQ109UWVbd109UWVbTF09UWVbeF09UWVbQV09UWVba109UWVbTV09UWVbal09UWVbRl09UWVbV109UWVbVV09ITAsUWVbbV09UWVbYl09UWVbUl09ITE7dmFyIGV0PXsiXFwiOiJcXCIsIiciOiInIiwiXG4iOiJuIiwiXHIiOiJyIiwiXHUyMDI4IjoidTIwMjgiLCJcdTIwMjkiOiJ1MjAyOSJ9LHR0PXBhcnNlRmxvYXQscnQ9cGFyc2VJbnQsaXQ9Im9iamVjdCI9PXR5cGVvZiByLmcmJnIuZyYmci5nLk9iamVjdD09PU9iamVjdCYmci5nLG50PSJvYmplY3QiPT10eXBlb2Ygc2VsZiYmc2VsZiYmc2VsZi5PYmplY3Q9PT1PYmplY3QmJnNlbGYsb3Q9aXR8fG50fHxGdW5jdGlvbigicmV0dXJuIHRoaXMiKSgpLHN0PXQmJiF0Lm5vZGVUeXBlJiZ0LGF0PXN0JiZlJiYhZS5ub2RlVHlwZSYmZSxjdD1hdCYmYXQuZXhwb3J0cz09PXN0LGx0PWN0JiZpdC5wcm9jZXNzLHV0PWZ1bmN0aW9uKCl7dHJ5e3JldHVybiBhdCYmYXQucmVxdWlyZSYmYXQucmVxdWlyZSgidXRpbCIpLnR5cGVzfHxsdCYmbHQuYmluZGluZyYmbHQuYmluZGluZygidXRpbCIpfWNhdGNoKGUpe319KCksaHQ9dXQmJnV0LmlzQXJyYXlCdWZmZXIsZnQ9dXQmJnV0LmlzRGF0ZSxfdD11dCYmdXQuaXNNYXAsZHQ9dXQmJnV0LmlzUmVnRXhwLHB0PXV0JiZ1dC5pc1NldCx2dD11dCYmdXQuaXNUeXBlZEFycmF5O2Z1bmN0aW9uIGd0KGUsdCxyKXtzd2l0Y2goci5sZW5ndGgpe2Nhc2UgMDpyZXR1cm4gZS5jYWxsKHQpO2Nhc2UgMTpyZXR1cm4gZS5jYWxsKHQsclswXSk7Y2FzZSAyOnJldHVybiBlLmNhbGwodCxyWzBdLHJbMV0pO2Nhc2UgMzpyZXR1cm4gZS5jYWxsKHQsclswXSxyWzFdLHJbMl0pfXJldHVybiBlLmFwcGx5KHQscil9ZnVuY3Rpb24geXQoZSx0LHIsaSl7Zm9yKHZhciBuPS0xLG89bnVsbD09ZT8wOmUubGVuZ3RoOysrbjxvOyl7dmFyIHM9ZVtuXTt0KGkscyxyKHMpLGUpfXJldHVybiBpfWZ1bmN0aW9uIG10KGUsdCl7Zm9yKHZhciByPS0xLGk9bnVsbD09ZT8wOmUubGVuZ3RoOysrcjxpJiYhMSE9PXQoZVtyXSxyLGUpOyk7cmV0dXJuIGV9ZnVuY3Rpb24gYnQoZSx0KXtmb3IodmFyIHI9bnVsbD09ZT8wOmUubGVuZ3RoO3ItLSYmITEhPT10KGVbcl0scixlKTspO3JldHVybiBlfWZ1bmN0aW9uIFN0KGUsdCl7Zm9yKHZhciByPS0xLGk9bnVsbD09ZT8wOmUubGVuZ3RoOysrcjxpOylpZighdChlW3JdLHIsZSkpcmV0dXJuITE7cmV0dXJuITB9ZnVuY3Rpb24gQ3QoZSx0KXtmb3IodmFyIHI9LTEsaT1udWxsPT1lPzA6ZS5sZW5ndGgsbj0wLG89W107KytyPGk7KXt2YXIgcz1lW3JdO3QocyxyLGUpJiYob1tuKytdPXMpfXJldHVybiBvfWZ1bmN0aW9uIHd0KGUsdCl7cmV0dXJuIShudWxsPT1lfHwhZS5sZW5ndGgpJiZCdChlLHQsMCk+LTF9ZnVuY3Rpb24gTHQoZSx0LHIpe2Zvcih2YXIgaT0tMSxuPW51bGw9PWU/MDplLmxlbmd0aDsrK2k8bjspaWYocih0LGVbaV0pKXJldHVybiEwO3JldHVybiExfWZ1bmN0aW9uIEV0KGUsdCl7Zm9yKHZhciByPS0xLGk9bnVsbD09ZT8wOmUubGVuZ3RoLG49QXJyYXkoaSk7KytyPGk7KW5bcl09dChlW3JdLHIsZSk7cmV0dXJuIG59ZnVuY3Rpb24geHQoZSx0KXtmb3IodmFyIHI9LTEsaT10Lmxlbmd0aCxuPWUubGVuZ3RoOysrcjxpOyllW24rcl09dFtyXTtyZXR1cm4gZX1mdW5jdGlvbiBBdChlLHQscixpKXt2YXIgbj0tMSxvPW51bGw9PWU/MDplLmxlbmd0aDtmb3IoaSYmbyYmKHI9ZVsrK25dKTsrK248bzspcj10KHIsZVtuXSxuLGUpO3JldHVybiByfWZ1bmN0aW9uIGt0KGUsdCxyLGkpe3ZhciBuPW51bGw9PWU/MDplLmxlbmd0aDtmb3IoaSYmbiYmKHI9ZVstLW5dKTtuLS07KXI9dChyLGVbbl0sbixlKTtyZXR1cm4gcn1mdW5jdGlvbiBNdChlLHQpe2Zvcih2YXIgcj0tMSxpPW51bGw9PWU/MDplLmxlbmd0aDsrK3I8aTspaWYodChlW3JdLHIsZSkpcmV0dXJuITA7cmV0dXJuITF9dmFyIFJ0PUh0KCJsZW5ndGgiKTtmdW5jdGlvbiBUdChlLHQscil7dmFyIGk7cmV0dXJuIHIoZSwoZnVuY3Rpb24oZSxyLG4pe2lmKHQoZSxyLG4pKXJldHVybiBpPXIsITF9KSksaX1mdW5jdGlvbiBPdChlLHQscixpKXtmb3IodmFyIG49ZS5sZW5ndGgsbz1yKyhpPzE6LTEpO2k/by0tOisrbzxuOylpZih0KGVbb10sbyxlKSlyZXR1cm4gbztyZXR1cm4tMX1mdW5jdGlvbiBCdChlLHQscil7cmV0dXJuIHQ9PXQ/ZnVuY3Rpb24oZSx0LHIpe2Zvcih2YXIgaT1yLTEsbj1lLmxlbmd0aDsrK2k8bjspaWYoZVtpXT09PXQpcmV0dXJuIGk7cmV0dXJuLTF9KGUsdCxyKTpPdChlLFB0LHIpfWZ1bmN0aW9uIER0KGUsdCxyLGkpe2Zvcih2YXIgbj1yLTEsbz1lLmxlbmd0aDsrK248bzspaWYoaShlW25dLHQpKXJldHVybiBuO3JldHVybi0xfWZ1bmN0aW9uIFB0KGUpe3JldHVybiBlIT1lfWZ1bmN0aW9uIEl0KGUsdCl7dmFyIHI9bnVsbD09ZT8wOmUubGVuZ3RoO3JldHVybiByP1d0KGUsdCkvcjpmfWZ1bmN0aW9uIEh0KGUpe3JldHVybiBmdW5jdGlvbih0KXtyZXR1cm4gbnVsbD09dD9uOnRbZV19fWZ1bmN0aW9uIGp0KGUpe3JldHVybiBmdW5jdGlvbih0KXtyZXR1cm4gbnVsbD09ZT9uOmVbdF19fWZ1bmN0aW9uIEZ0KGUsdCxyLGksbil7cmV0dXJuIG4oZSwoZnVuY3Rpb24oZSxuLG8pe3I9aT8oaT0hMSxlKTp0KHIsZSxuLG8pfSkpLHJ9ZnVuY3Rpb24gV3QoZSx0KXtmb3IodmFyIHIsaT0tMSxvPWUubGVuZ3RoOysraTxvOyl7dmFyIHM9dChlW2ldKTtzIT09biYmKHI9cj09PW4/czpyK3MpfXJldHVybiByfWZ1bmN0aW9uIFV0KGUsdCl7Zm9yKHZhciByPS0xLGk9QXJyYXkoZSk7KytyPGU7KWlbcl09dChyKTtyZXR1cm4gaX1mdW5jdGlvbiBxdChlKXtyZXR1cm4gZT9lLnNsaWNlKDAsc3IoZSkrMSkucmVwbGFjZShpZSwiIik6ZX1mdW5jdGlvbiBOdChlKXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIGUodCl9fWZ1bmN0aW9uIHp0KGUsdCl7cmV0dXJuIEV0KHQsKGZ1bmN0aW9uKHQpe3JldHVybiBlW3RdfSkpfWZ1bmN0aW9uIEt0KGUsdCl7cmV0dXJuIGUuaGFzKHQpfWZ1bmN0aW9uIFZ0KGUsdCl7Zm9yKHZhciByPS0xLGk9ZS5sZW5ndGg7KytyPGkmJkJ0KHQsZVtyXSwwKT4tMTspO3JldHVybiByfWZ1bmN0aW9uIEd0KGUsdCl7Zm9yKHZhciByPWUubGVuZ3RoO3ItLSYmQnQodCxlW3JdLDApPi0xOyk7cmV0dXJuIHJ9ZnVuY3Rpb24gWXQoZSx0KXtmb3IodmFyIHI9ZS5sZW5ndGgsaT0wO3ItLTspZVtyXT09PXQmJisraTtyZXR1cm4gaX12YXIgWHQ9anQoe8OAOiJBIizDgToiQSIsw4I6IkEiLMODOiJBIizDhDoiQSIsw4U6IkEiLMOgOiJhIizDoToiYSIsw6I6ImEiLMOjOiJhIizDpDoiYSIsw6U6ImEiLMOHOiJDIizDpzoiYyIsw5A6IkQiLMOwOiJkIizDiDoiRSIsw4k6IkUiLMOKOiJFIizDizoiRSIsw6g6ImUiLMOpOiJlIizDqjoiZSIsw6s6ImUiLMOMOiJJIizDjToiSSIsw446IkkiLMOPOiJJIizDrDoiaSIsw606ImkiLMOuOiJpIizDrzoiaSIsw5E6Ik4iLMOxOiJuIizDkjoiTyIsw5M6Ik8iLMOUOiJPIizDlToiTyIsw5Y6Ik8iLMOYOiJPIizDsjoibyIsw7M6Im8iLMO0OiJvIizDtToibyIsw7Y6Im8iLMO4OiJvIizDmToiVSIsw5o6IlUiLMObOiJVIizDnDoiVSIsw7k6InUiLMO6OiJ1IizDuzoidSIsw7w6InUiLMOdOiJZIizDvToieSIsw786InkiLMOGOiJBZSIsw6Y6ImFlIizDnjoiVGgiLMO+OiJ0aCIsw586InNzIizEgDoiQSIsxII6IkEiLMSEOiJBIizEgToiYSIsxIM6ImEiLMSFOiJhIizEhjoiQyIsxIg6IkMiLMSKOiJDIizEjDoiQyIsxIc6ImMiLMSJOiJjIizEizoiYyIsxI06ImMiLMSOOiJEIizEkDoiRCIsxI86ImQiLMSROiJkIizEkjoiRSIsxJQ6IkUiLMSWOiJFIizEmDoiRSIsxJo6IkUiLMSTOiJlIizElToiZSIsxJc6ImUiLMSZOiJlIizEmzoiZSIsxJw6IkciLMSeOiJHIizEoDoiRyIsxKI6IkciLMSdOiJnIizEnzoiZyIsxKE6ImciLMSjOiJnIizEpDoiSCIsxKY6IkgiLMSlOiJoIizEpzoiaCIsxKg6IkkiLMSqOiJJIizErDoiSSIsxK46IkkiLMSwOiJJIizEqToiaSIsxKs6ImkiLMStOiJpIizErzoiaSIsxLE6ImkiLMS0OiJKIizEtToiaiIsxLY6IksiLMS3OiJrIizEuDoiayIsxLk6IkwiLMS7OiJMIizEvToiTCIsxL86IkwiLMWBOiJMIizEujoibCIsxLw6ImwiLMS+OiJsIizFgDoibCIsxYI6ImwiLMWDOiJOIizFhToiTiIsxYc6Ik4iLMWKOiJOIizFhDoibiIsxYY6Im4iLMWIOiJuIizFizoibiIsxYw6Ik8iLMWOOiJPIizFkDoiTyIsxY06Im8iLMWPOiJvIizFkToibyIsxZQ6IlIiLMWWOiJSIizFmDoiUiIsxZU6InIiLMWXOiJyIizFmToiciIsxZo6IlMiLMWcOiJTIizFnjoiUyIsxaA6IlMiLMWbOiJzIizFnToicyIsxZ86InMiLMWhOiJzIizFojoiVCIsxaQ6IlQiLMWmOiJUIizFozoidCIsxaU6InQiLMWnOiJ0IizFqDoiVSIsxao6IlUiLMWsOiJVIizFrjoiVSIsxbA6IlUiLMWyOiJVIizFqToidSIsxas6InUiLMWtOiJ1IizFrzoidSIsxbE6InUiLMWzOiJ1IizFtDoiVyIsxbU6InciLMW2OiJZIizFtzoieSIsxbg6IlkiLMW5OiJaIizFuzoiWiIsxb06IloiLMW6OiJ6IizFvDoieiIsxb46InoiLMSyOiJJSiIsxLM6ImlqIizFkjoiT2UiLMWTOiJvZSIsxYk6IiduIizFvzoicyJ9KSxadD1qdCh7IiYiOiImYW1wOyIsIjwiOiImbHQ7IiwiPiI6IiZndDsiLCciJzoiJnF1b3Q7IiwiJyI6IiYjMzk7In0pO2Z1bmN0aW9uIEp0KGUpe3JldHVybiJcXCIrZXRbZV19ZnVuY3Rpb24gJHQoZSl7cmV0dXJuIFllLnRlc3QoZSl9ZnVuY3Rpb24gUXQoZSl7dmFyIHQ9LTEscj1BcnJheShlLnNpemUpO3JldHVybiBlLmZvckVhY2goKGZ1bmN0aW9uKGUsaSl7clsrK3RdPVtpLGVdfSkpLHJ9ZnVuY3Rpb24gZXIoZSx0KXtyZXR1cm4gZnVuY3Rpb24ocil7cmV0dXJuIGUodChyKSl9fWZ1bmN0aW9uIHRyKGUsdCl7Zm9yKHZhciByPS0xLGk9ZS5sZW5ndGgsbj0wLG89W107KytyPGk7KXt2YXIgcz1lW3JdO3MhPT10JiZzIT09YXx8KGVbcl09YSxvW24rK109cil9cmV0dXJuIG99ZnVuY3Rpb24gcnIoZSl7dmFyIHQ9LTEscj1BcnJheShlLnNpemUpO3JldHVybiBlLmZvckVhY2goKGZ1bmN0aW9uKGUpe3JbKyt0XT1lfSkpLHJ9ZnVuY3Rpb24gaXIoZSl7dmFyIHQ9LTEscj1BcnJheShlLnNpemUpO3JldHVybiBlLmZvckVhY2goKGZ1bmN0aW9uKGUpe3JbKyt0XT1bZSxlXX0pKSxyfWZ1bmN0aW9uIG5yKGUpe3JldHVybiAkdChlKT9mdW5jdGlvbihlKXtmb3IodmFyIHQ9VmUubGFzdEluZGV4PTA7VmUudGVzdChlKTspKyt0O3JldHVybiB0fShlKTpSdChlKX1mdW5jdGlvbiBvcihlKXtyZXR1cm4gJHQoZSk/ZnVuY3Rpb24oZSl7cmV0dXJuIGUubWF0Y2goVmUpfHxbXX0oZSk6ZnVuY3Rpb24oZSl7cmV0dXJuIGUuc3BsaXQoIiIpfShlKX1mdW5jdGlvbiBzcihlKXtmb3IodmFyIHQ9ZS5sZW5ndGg7dC0tJiZuZS50ZXN0KGUuY2hhckF0KHQpKTspO3JldHVybiB0fXZhciBhcj1qdCh7IiZhbXA7IjoiJiIsIiZsdDsiOiI8IiwiJmd0OyI6Ij4iLCImcXVvdDsiOiciJywiJiMzOTsiOiInIn0pLGNyPWZ1bmN0aW9uIGUodCl7dmFyIHIsaT0odD1udWxsPT10P290OmNyLmRlZmF1bHRzKG90Lk9iamVjdCgpLHQsY3IucGljayhvdCxaZSkpKS5BcnJheSxuZT10LkRhdGUsU2U9dC5FcnJvcixDZT10LkZ1bmN0aW9uLHdlPXQuTWF0aCxMZT10Lk9iamVjdCxFZT10LlJlZ0V4cCx4ZT10LlN0cmluZyxBZT10LlR5cGVFcnJvcixrZT1pLnByb3RvdHlwZSxNZT1DZS5wcm90b3R5cGUsUmU9TGUucHJvdG90eXBlLFRlPXRbIl9fY29yZS1qc19zaGFyZWRfXyJdLE9lPU1lLnRvU3RyaW5nLEJlPVJlLmhhc093blByb3BlcnR5LERlPTAsUGU9KHI9L1teLl0rJC8uZXhlYyhUZSYmVGUua2V5cyYmVGUua2V5cy5JRV9QUk9UT3x8IiIpKT8iU3ltYm9sKHNyYylfMS4iK3I6IiIsSWU9UmUudG9TdHJpbmcsSGU9T2UuY2FsbChMZSksamU9b3QuXyxGZT1FZSgiXiIrT2UuY2FsbChCZSkucmVwbGFjZSh0ZSwiXFwkJiIpLnJlcGxhY2UoL2hhc093blByb3BlcnR5fChmdW5jdGlvbikuKj8oPz1cXFwoKXwgZm9yIC4rPyg/PVxcXF0pL2csIiQxLio/IikrIiQiKSxXZT1jdD90LkJ1ZmZlcjpuLFVlPXQuU3ltYm9sLHFlPXQuVWludDhBcnJheSxOZT1XZT9XZS5hbGxvY1Vuc2FmZTpuLFZlPWVyKExlLmdldFByb3RvdHlwZU9mLExlKSxZZT1MZS5jcmVhdGUsZXQ9UmUucHJvcGVydHlJc0VudW1lcmFibGUsaXQ9a2Uuc3BsaWNlLG50PVVlP1VlLmlzQ29uY2F0U3ByZWFkYWJsZTpuLHN0PVVlP1VlLml0ZXJhdG9yOm4sYXQ9VWU/VWUudG9TdHJpbmdUYWc6bixsdD1mdW5jdGlvbigpe3RyeXt2YXIgZT1sbyhMZSwiZGVmaW5lUHJvcGVydHkiKTtyZXR1cm4gZSh7fSwiIix7fSksZX1jYXRjaChlKXt9fSgpLHV0PXQuY2xlYXJUaW1lb3V0IT09b3QuY2xlYXJUaW1lb3V0JiZ0LmNsZWFyVGltZW91dCxSdD1uZSYmbmUubm93IT09b3QuRGF0ZS5ub3cmJm5lLm5vdyxqdD10LnNldFRpbWVvdXQhPT1vdC5zZXRUaW1lb3V0JiZ0LnNldFRpbWVvdXQsbHI9d2UuY2VpbCx1cj13ZS5mbG9vcixocj1MZS5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMsZnI9V2U/V2UuaXNCdWZmZXI6bixfcj10LmlzRmluaXRlLGRyPWtlLmpvaW4scHI9ZXIoTGUua2V5cyxMZSksdnI9d2UubWF4LGdyPXdlLm1pbix5cj1uZS5ub3csbXI9dC5wYXJzZUludCxicj13ZS5yYW5kb20sU3I9a2UucmV2ZXJzZSxDcj1sbyh0LCJEYXRhVmlldyIpLHdyPWxvKHQsIk1hcCIpLExyPWxvKHQsIlByb21pc2UiKSxFcj1sbyh0LCJTZXQiKSx4cj1sbyh0LCJXZWFrTWFwIiksQXI9bG8oTGUsImNyZWF0ZSIpLGtyPXhyJiZuZXcgeHIsTXI9e30sUnI9Rm8oQ3IpLFRyPUZvKHdyKSxPcj1GbyhMciksQnI9Rm8oRXIpLERyPUZvKHhyKSxQcj1VZT9VZS5wcm90b3R5cGU6bixJcj1Qcj9Qci52YWx1ZU9mOm4sSHI9UHI/UHIudG9TdHJpbmc6bjtmdW5jdGlvbiBqcihlKXtpZihyYShlKSYmIUtzKGUpJiYhKGUgaW5zdGFuY2VvZiBxcikpe2lmKGUgaW5zdGFuY2VvZiBVcilyZXR1cm4gZTtpZihCZS5jYWxsKGUsIl9fd3JhcHBlZF9fIikpcmV0dXJuIFdvKGUpfXJldHVybiBuZXcgVXIoZSl9dmFyIEZyPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe31yZXR1cm4gZnVuY3Rpb24odCl7aWYoIXRhKHQpKXJldHVybnt9O2lmKFllKXJldHVybiBZZSh0KTtlLnByb3RvdHlwZT10O3ZhciByPW5ldyBlO3JldHVybiBlLnByb3RvdHlwZT1uLHJ9fSgpO2Z1bmN0aW9uIFdyKCl7fWZ1bmN0aW9uIFVyKGUsdCl7dGhpcy5fX3dyYXBwZWRfXz1lLHRoaXMuX19hY3Rpb25zX189W10sdGhpcy5fX2NoYWluX189ISF0LHRoaXMuX19pbmRleF9fPTAsdGhpcy5fX3ZhbHVlc19fPW59ZnVuY3Rpb24gcXIoZSl7dGhpcy5fX3dyYXBwZWRfXz1lLHRoaXMuX19hY3Rpb25zX189W10sdGhpcy5fX2Rpcl9fPTEsdGhpcy5fX2ZpbHRlcmVkX189ITEsdGhpcy5fX2l0ZXJhdGVlc19fPVtdLHRoaXMuX190YWtlQ291bnRfXz1fLHRoaXMuX192aWV3c19fPVtdfWZ1bmN0aW9uIE5yKGUpe3ZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoO2Zvcih0aGlzLmNsZWFyKCk7Kyt0PHI7KXt2YXIgaT1lW3RdO3RoaXMuc2V0KGlbMF0saVsxXSl9fWZ1bmN0aW9uIHpyKGUpe3ZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoO2Zvcih0aGlzLmNsZWFyKCk7Kyt0PHI7KXt2YXIgaT1lW3RdO3RoaXMuc2V0KGlbMF0saVsxXSl9fWZ1bmN0aW9uIEtyKGUpe3ZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoO2Zvcih0aGlzLmNsZWFyKCk7Kyt0PHI7KXt2YXIgaT1lW3RdO3RoaXMuc2V0KGlbMF0saVsxXSl9fWZ1bmN0aW9uIFZyKGUpe3ZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoO2Zvcih0aGlzLl9fZGF0YV9fPW5ldyBLcjsrK3Q8cjspdGhpcy5hZGQoZVt0XSl9ZnVuY3Rpb24gR3IoZSl7dmFyIHQ9dGhpcy5fX2RhdGFfXz1uZXcgenIoZSk7dGhpcy5zaXplPXQuc2l6ZX1mdW5jdGlvbiBZcihlLHQpe3ZhciByPUtzKGUpLGk9IXImJnpzKGUpLG49IXImJiFpJiZYcyhlKSxvPSFyJiYhaSYmIW4mJnVhKGUpLHM9cnx8aXx8bnx8byxhPXM/VXQoZS5sZW5ndGgseGUpOltdLGM9YS5sZW5ndGg7Zm9yKHZhciBsIGluIGUpIXQmJiFCZS5jYWxsKGUsbCl8fHMmJigibGVuZ3RoIj09bHx8biYmKCJvZmZzZXQiPT1sfHwicGFyZW50Ij09bCl8fG8mJigiYnVmZmVyIj09bHx8ImJ5dGVMZW5ndGgiPT1sfHwiYnl0ZU9mZnNldCI9PWwpfHxnbyhsLGMpKXx8YS5wdXNoKGwpO3JldHVybiBhfWZ1bmN0aW9uIFhyKGUpe3ZhciB0PWUubGVuZ3RoO3JldHVybiB0P2VbS2koMCx0LTEpXTpufWZ1bmN0aW9uIFpyKGUsdCl7cmV0dXJuIERvKEFuKGUpLG9pKHQsMCxlLmxlbmd0aCkpfWZ1bmN0aW9uIEpyKGUpe3JldHVybiBEbyhBbihlKSl9ZnVuY3Rpb24gJHIoZSx0LHIpeyhyIT09biYmIVVzKGVbdF0scil8fHI9PT1uJiYhKHQgaW4gZSkpJiZpaShlLHQscil9ZnVuY3Rpb24gUXIoZSx0LHIpe3ZhciBpPWVbdF07QmUuY2FsbChlLHQpJiZVcyhpLHIpJiYociE9PW58fHQgaW4gZSl8fGlpKGUsdCxyKX1mdW5jdGlvbiBlaShlLHQpe2Zvcih2YXIgcj1lLmxlbmd0aDtyLS07KWlmKFVzKGVbcl1bMF0sdCkpcmV0dXJuIHI7cmV0dXJuLTF9ZnVuY3Rpb24gdGkoZSx0LHIsaSl7cmV0dXJuIHVpKGUsKGZ1bmN0aW9uKGUsbixvKXt0KGksZSxyKGUpLG8pfSkpLGl9ZnVuY3Rpb24gcmkoZSx0KXtyZXR1cm4gZSYma24odCxPYSh0KSxlKX1mdW5jdGlvbiBpaShlLHQscil7Il9fcHJvdG9fXyI9PXQmJmx0P2x0KGUsdCx7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsdmFsdWU6cix3cml0YWJsZTohMH0pOmVbdF09cn1mdW5jdGlvbiBuaShlLHQpe2Zvcih2YXIgcj0tMSxvPXQubGVuZ3RoLHM9aShvKSxhPW51bGw9PWU7KytyPG87KXNbcl09YT9uOkFhKGUsdFtyXSk7cmV0dXJuIHN9ZnVuY3Rpb24gb2koZSx0LHIpe3JldHVybiBlPT1lJiYociE9PW4mJihlPWU8PXI/ZTpyKSx0IT09biYmKGU9ZT49dD9lOnQpKSxlfWZ1bmN0aW9uIHNpKGUsdCxyLGksbyxzKXt2YXIgYSxjPTEmdCxsPTImdCx1PTQmdDtpZihyJiYoYT1vP3IoZSxpLG8scyk6cihlKSksYSE9PW4pcmV0dXJuIGE7aWYoIXRhKGUpKXJldHVybiBlO3ZhciBoPUtzKGUpO2lmKGgpe2lmKGE9ZnVuY3Rpb24oZSl7dmFyIHQ9ZS5sZW5ndGgscj1uZXcgZS5jb25zdHJ1Y3Rvcih0KTtyZXR1cm4gdCYmInN0cmluZyI9PXR5cGVvZiBlWzBdJiZCZS5jYWxsKGUsImluZGV4IikmJihyLmluZGV4PWUuaW5kZXgsci5pbnB1dD1lLmlucHV0KSxyfShlKSwhYylyZXR1cm4gQW4oZSxhKX1lbHNle3ZhciBmPWZvKGUpLF89Zj09Ynx8Zj09UztpZihYcyhlKSlyZXR1cm4gU24oZSxjKTtpZihmPT1MfHxmPT1wfHxfJiYhbyl7aWYoYT1sfHxfP3t9OnBvKGUpLCFjKXJldHVybiBsP2Z1bmN0aW9uKGUsdCl7cmV0dXJuIGtuKGUsaG8oZSksdCl9KGUsZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYma24odCxCYSh0KSxlKX0oYSxlKSk6ZnVuY3Rpb24oZSx0KXtyZXR1cm4ga24oZSx1byhlKSx0KX0oZSxyaShhLGUpKX1lbHNle2lmKCFRZVtmXSlyZXR1cm4gbz9lOnt9O2E9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49ZS5jb25zdHJ1Y3Rvcjtzd2l0Y2godCl7Y2FzZSBUOnJldHVybiBDbihlKTtjYXNlIGc6Y2FzZSB5OnJldHVybiBuZXcgbigrZSk7Y2FzZSBPOnJldHVybiBmdW5jdGlvbihlLHQpe3ZhciByPXQ/Q24oZS5idWZmZXIpOmUuYnVmZmVyO3JldHVybiBuZXcgZS5jb25zdHJ1Y3RvcihyLGUuYnl0ZU9mZnNldCxlLmJ5dGVMZW5ndGgpfShlLHIpO2Nhc2UgQjpjYXNlIEQ6Y2FzZSBQOmNhc2UgSTpjYXNlIEg6Y2FzZSBqOmNhc2UgRjpjYXNlIFc6Y2FzZSBVOnJldHVybiB3bihlLHIpO2Nhc2UgQzpyZXR1cm4gbmV3IG47Y2FzZSB3OmNhc2UgazpyZXR1cm4gbmV3IG4oZSk7Y2FzZSB4OnJldHVybiBmdW5jdGlvbihlKXt2YXIgdD1uZXcgZS5jb25zdHJ1Y3RvcihlLnNvdXJjZSxmZS5leGVjKGUpKTtyZXR1cm4gdC5sYXN0SW5kZXg9ZS5sYXN0SW5kZXgsdH0oZSk7Y2FzZSBBOnJldHVybiBuZXcgbjtjYXNlIE06cmV0dXJuIGk9ZSxJcj9MZShJci5jYWxsKGkpKTp7fX19KGUsZixjKX19c3x8KHM9bmV3IEdyKTt2YXIgZD1zLmdldChlKTtpZihkKXJldHVybiBkO3Muc2V0KGUsYSksYWEoZSk/ZS5mb3JFYWNoKChmdW5jdGlvbihpKXthLmFkZChzaShpLHQscixpLGUscykpfSkpOmlhKGUpJiZlLmZvckVhY2goKGZ1bmN0aW9uKGksbil7YS5zZXQobixzaShpLHQscixuLGUscykpfSkpO3ZhciB2PWg/bjoodT9sP3JvOnRvOmw/QmE6T2EpKGUpO3JldHVybiBtdCh2fHxlLChmdW5jdGlvbihpLG4pe3YmJihpPWVbbj1pXSksUXIoYSxuLHNpKGksdCxyLG4sZSxzKSl9KSksYX1mdW5jdGlvbiBhaShlLHQscil7dmFyIGk9ci5sZW5ndGg7aWYobnVsbD09ZSlyZXR1cm4haTtmb3IoZT1MZShlKTtpLS07KXt2YXIgbz1yW2ldLHM9dFtvXSxhPWVbb107aWYoYT09PW4mJiEobyBpbiBlKXx8IXMoYSkpcmV0dXJuITF9cmV0dXJuITB9ZnVuY3Rpb24gY2koZSx0LHIpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiBlKXRocm93IG5ldyBBZShvKTtyZXR1cm4gUm8oKGZ1bmN0aW9uKCl7ZS5hcHBseShuLHIpfSksdCl9ZnVuY3Rpb24gbGkoZSx0LHIsaSl7dmFyIG49LTEsbz13dCxzPSEwLGE9ZS5sZW5ndGgsYz1bXSxsPXQubGVuZ3RoO2lmKCFhKXJldHVybiBjO3ImJih0PUV0KHQsTnQocikpKSxpPyhvPUx0LHM9ITEpOnQubGVuZ3RoPj0yMDAmJihvPUt0LHM9ITEsdD1uZXcgVnIodCkpO2U6Zm9yKDsrK248YTspe3ZhciB1PWVbbl0saD1udWxsPT1yP3U6cih1KTtpZih1PWl8fDAhPT11P3U6MCxzJiZoPT1oKXtmb3IodmFyIGY9bDtmLS07KWlmKHRbZl09PT1oKWNvbnRpbnVlIGU7Yy5wdXNoKHUpfWVsc2Ugbyh0LGgsaSl8fGMucHVzaCh1KX1yZXR1cm4gY31qci50ZW1wbGF0ZVNldHRpbmdzPXtlc2NhcGU6WCxldmFsdWF0ZTpaLGludGVycG9sYXRlOkosdmFyaWFibGU6IiIsaW1wb3J0czp7Xzpqcn19LGpyLnByb3RvdHlwZT1Xci5wcm90b3R5cGUsanIucHJvdG90eXBlLmNvbnN0cnVjdG9yPWpyLFVyLnByb3RvdHlwZT1GcihXci5wcm90b3R5cGUpLFVyLnByb3RvdHlwZS5jb25zdHJ1Y3Rvcj1Vcixxci5wcm90b3R5cGU9RnIoV3IucHJvdG90eXBlKSxxci5wcm90b3R5cGUuY29uc3RydWN0b3I9cXIsTnIucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7dGhpcy5fX2RhdGFfXz1Bcj9BcihudWxsKTp7fSx0aGlzLnNpemU9MH0sTnIucHJvdG90eXBlLmRlbGV0ZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzLmhhcyhlKSYmZGVsZXRlIHRoaXMuX19kYXRhX19bZV07cmV0dXJuIHRoaXMuc2l6ZS09dD8xOjAsdH0sTnIucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9fZGF0YV9fO2lmKEFyKXt2YXIgcj10W2VdO3JldHVybiByPT09cz9uOnJ9cmV0dXJuIEJlLmNhbGwodCxlKT90W2VdOm59LE5yLnByb3RvdHlwZS5oYXM9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fX2RhdGFfXztyZXR1cm4gQXI/dFtlXSE9PW46QmUuY2FsbCh0LGUpfSxOci5wcm90b3R5cGUuc2V0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5fX2RhdGFfXztyZXR1cm4gdGhpcy5zaXplKz10aGlzLmhhcyhlKT8wOjEscltlXT1BciYmdD09PW4/czp0LHRoaXN9LHpyLnByb3RvdHlwZS5jbGVhcj1mdW5jdGlvbigpe3RoaXMuX19kYXRhX189W10sdGhpcy5zaXplPTB9LHpyLnByb3RvdHlwZS5kZWxldGU9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fX2RhdGFfXyxyPWVpKHQsZSk7cmV0dXJuIShyPDB8fChyPT10Lmxlbmd0aC0xP3QucG9wKCk6aXQuY2FsbCh0LHIsMSksLS10aGlzLnNpemUsMCkpfSx6ci5wcm90b3R5cGUuZ2V0PWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMuX19kYXRhX18scj1laSh0LGUpO3JldHVybiByPDA/bjp0W3JdWzFdfSx6ci5wcm90b3R5cGUuaGFzPWZ1bmN0aW9uKGUpe3JldHVybiBlaSh0aGlzLl9fZGF0YV9fLGUpPi0xfSx6ci5wcm90b3R5cGUuc2V0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5fX2RhdGFfXyxpPWVpKHIsZSk7cmV0dXJuIGk8MD8oKyt0aGlzLnNpemUsci5wdXNoKFtlLHRdKSk6cltpXVsxXT10LHRoaXN9LEtyLnByb3RvdHlwZS5jbGVhcj1mdW5jdGlvbigpe3RoaXMuc2l6ZT0wLHRoaXMuX19kYXRhX189e2hhc2g6bmV3IE5yLG1hcDpuZXcod3J8fHpyKSxzdHJpbmc6bmV3IE5yfX0sS3IucHJvdG90eXBlLmRlbGV0ZT1mdW5jdGlvbihlKXt2YXIgdD1hbyh0aGlzLGUpLmRlbGV0ZShlKTtyZXR1cm4gdGhpcy5zaXplLT10PzE6MCx0fSxLci5wcm90b3R5cGUuZ2V0PWZ1bmN0aW9uKGUpe3JldHVybiBhbyh0aGlzLGUpLmdldChlKX0sS3IucHJvdG90eXBlLmhhcz1mdW5jdGlvbihlKXtyZXR1cm4gYW8odGhpcyxlKS5oYXMoZSl9LEtyLnByb3RvdHlwZS5zZXQ9ZnVuY3Rpb24oZSx0KXt2YXIgcj1hbyh0aGlzLGUpLGk9ci5zaXplO3JldHVybiByLnNldChlLHQpLHRoaXMuc2l6ZSs9ci5zaXplPT1pPzA6MSx0aGlzfSxWci5wcm90b3R5cGUuYWRkPVZyLnByb3RvdHlwZS5wdXNoPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9fZGF0YV9fLnNldChlLHMpLHRoaXN9LFZyLnByb3RvdHlwZS5oYXM9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX19kYXRhX18uaGFzKGUpfSxHci5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXt0aGlzLl9fZGF0YV9fPW5ldyB6cix0aGlzLnNpemU9MH0sR3IucHJvdG90eXBlLmRlbGV0ZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9fZGF0YV9fLHI9dC5kZWxldGUoZSk7cmV0dXJuIHRoaXMuc2l6ZT10LnNpemUscn0sR3IucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fX2RhdGFfXy5nZXQoZSl9LEdyLnByb3RvdHlwZS5oYXM9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX19kYXRhX18uaGFzKGUpfSxHci5wcm90b3R5cGUuc2V0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5fX2RhdGFfXztpZihyIGluc3RhbmNlb2YgenIpe3ZhciBpPXIuX19kYXRhX187aWYoIXdyfHxpLmxlbmd0aDwxOTkpcmV0dXJuIGkucHVzaChbZSx0XSksdGhpcy5zaXplPSsrci5zaXplLHRoaXM7cj10aGlzLl9fZGF0YV9fPW5ldyBLcihpKX1yZXR1cm4gci5zZXQoZSx0KSx0aGlzLnNpemU9ci5zaXplLHRoaXN9O3ZhciB1aT1Ubih5aSksaGk9VG4obWksITApO2Z1bmN0aW9uIGZpKGUsdCl7dmFyIHI9ITA7cmV0dXJuIHVpKGUsKGZ1bmN0aW9uKGUsaSxuKXtyZXR1cm4gcj0hIXQoZSxpLG4pfSkpLHJ9ZnVuY3Rpb24gX2koZSx0LHIpe2Zvcih2YXIgaT0tMSxvPWUubGVuZ3RoOysraTxvOyl7dmFyIHM9ZVtpXSxhPXQocyk7aWYobnVsbCE9YSYmKGM9PT1uP2E9PWEmJiFsYShhKTpyKGEsYykpKXZhciBjPWEsbD1zfXJldHVybiBsfWZ1bmN0aW9uIGRpKGUsdCl7dmFyIHI9W107cmV0dXJuIHVpKGUsKGZ1bmN0aW9uKGUsaSxuKXt0KGUsaSxuKSYmci5wdXNoKGUpfSkpLHJ9ZnVuY3Rpb24gcGkoZSx0LHIsaSxuKXt2YXIgbz0tMSxzPWUubGVuZ3RoO2ZvcihyfHwocj12byksbnx8KG49W10pOysrbzxzOyl7dmFyIGE9ZVtvXTt0PjAmJnIoYSk/dD4xP3BpKGEsdC0xLHIsaSxuKTp4dChuLGEpOml8fChuW24ubGVuZ3RoXT1hKX1yZXR1cm4gbn12YXIgdmk9T24oKSxnaT1PbighMCk7ZnVuY3Rpb24geWkoZSx0KXtyZXR1cm4gZSYmdmkoZSx0LE9hKX1mdW5jdGlvbiBtaShlLHQpe3JldHVybiBlJiZnaShlLHQsT2EpfWZ1bmN0aW9uIGJpKGUsdCl7cmV0dXJuIEN0KHQsKGZ1bmN0aW9uKHQpe3JldHVybiAkcyhlW3RdKX0pKX1mdW5jdGlvbiBTaShlLHQpe2Zvcih2YXIgcj0wLGk9KHQ9Z24odCxlKSkubGVuZ3RoO251bGwhPWUmJnI8aTspZT1lW2pvKHRbcisrXSldO3JldHVybiByJiZyPT1pP2U6bn1mdW5jdGlvbiBDaShlLHQscil7dmFyIGk9dChlKTtyZXR1cm4gS3MoZSk/aTp4dChpLHIoZSkpfWZ1bmN0aW9uIHdpKGUpe3JldHVybiBudWxsPT1lP2U9PT1uPyJbb2JqZWN0IFVuZGVmaW5lZF0iOiJbb2JqZWN0IE51bGxdIjphdCYmYXQgaW4gTGUoZSk/ZnVuY3Rpb24oZSl7dmFyIHQ9QmUuY2FsbChlLGF0KSxyPWVbYXRdO3RyeXtlW2F0XT1uO3ZhciBpPSEwfWNhdGNoKGUpe312YXIgbz1JZS5jYWxsKGUpO3JldHVybiBpJiYodD9lW2F0XT1yOmRlbGV0ZSBlW2F0XSksb30oZSk6ZnVuY3Rpb24oZSl7cmV0dXJuIEllLmNhbGwoZSl9KGUpfWZ1bmN0aW9uIExpKGUsdCl7cmV0dXJuIGU+dH1mdW5jdGlvbiBFaShlLHQpe3JldHVybiBudWxsIT1lJiZCZS5jYWxsKGUsdCl9ZnVuY3Rpb24geGkoZSx0KXtyZXR1cm4gbnVsbCE9ZSYmdCBpbiBMZShlKX1mdW5jdGlvbiBBaShlLHQscil7Zm9yKHZhciBvPXI/THQ6d3Qscz1lWzBdLmxlbmd0aCxhPWUubGVuZ3RoLGM9YSxsPWkoYSksdT0xLzAsaD1bXTtjLS07KXt2YXIgZj1lW2NdO2MmJnQmJihmPUV0KGYsTnQodCkpKSx1PWdyKGYubGVuZ3RoLHUpLGxbY109IXImJih0fHxzPj0xMjAmJmYubGVuZ3RoPj0xMjApP25ldyBWcihjJiZmKTpufWY9ZVswXTt2YXIgXz0tMSxkPWxbMF07ZTpmb3IoOysrXzxzJiZoLmxlbmd0aDx1Oyl7dmFyIHA9ZltfXSx2PXQ/dChwKTpwO2lmKHA9cnx8MCE9PXA/cDowLCEoZD9LdChkLHYpOm8oaCx2LHIpKSl7Zm9yKGM9YTstLWM7KXt2YXIgZz1sW2NdO2lmKCEoZz9LdChnLHYpOm8oZVtjXSx2LHIpKSljb250aW51ZSBlfWQmJmQucHVzaCh2KSxoLnB1c2gocCl9fXJldHVybiBofWZ1bmN0aW9uIGtpKGUsdCxyKXt2YXIgaT1udWxsPT0oZT14byhlLHQ9Z24odCxlKSkpP2U6ZVtqbyhKbyh0KSldO3JldHVybiBudWxsPT1pP246Z3QoaSxlLHIpfWZ1bmN0aW9uIE1pKGUpe3JldHVybiByYShlKSYmd2koZSk9PXB9ZnVuY3Rpb24gUmkoZSx0LHIsaSxvKXtyZXR1cm4gZT09PXR8fChudWxsPT1lfHxudWxsPT10fHwhcmEoZSkmJiFyYSh0KT9lIT1lJiZ0IT10OmZ1bmN0aW9uKGUsdCxyLGksbyxzKXt2YXIgYT1LcyhlKSxjPUtzKHQpLGw9YT92OmZvKGUpLHU9Yz92OmZvKHQpLGg9KGw9bD09cD9MOmwpPT1MLGY9KHU9dT09cD9MOnUpPT1MLF89bD09dTtpZihfJiZYcyhlKSl7aWYoIVhzKHQpKXJldHVybiExO2E9ITAsaD0hMX1pZihfJiYhaClyZXR1cm4gc3x8KHM9bmV3IEdyKSxhfHx1YShlKT9RbihlLHQscixpLG8scyk6ZnVuY3Rpb24oZSx0LHIsaSxuLG8scyl7c3dpdGNoKHIpe2Nhc2UgTzppZihlLmJ5dGVMZW5ndGghPXQuYnl0ZUxlbmd0aHx8ZS5ieXRlT2Zmc2V0IT10LmJ5dGVPZmZzZXQpcmV0dXJuITE7ZT1lLmJ1ZmZlcix0PXQuYnVmZmVyO2Nhc2UgVDpyZXR1cm4hKGUuYnl0ZUxlbmd0aCE9dC5ieXRlTGVuZ3RofHwhbyhuZXcgcWUoZSksbmV3IHFlKHQpKSk7Y2FzZSBnOmNhc2UgeTpjYXNlIHc6cmV0dXJuIFVzKCtlLCt0KTtjYXNlIG06cmV0dXJuIGUubmFtZT09dC5uYW1lJiZlLm1lc3NhZ2U9PXQubWVzc2FnZTtjYXNlIHg6Y2FzZSBrOnJldHVybiBlPT10KyIiO2Nhc2UgQzp2YXIgYT1RdDtjYXNlIEE6dmFyIGM9MSZpO2lmKGF8fChhPXJyKSxlLnNpemUhPXQuc2l6ZSYmIWMpcmV0dXJuITE7dmFyIGw9cy5nZXQoZSk7aWYobClyZXR1cm4gbD09dDtpfD0yLHMuc2V0KGUsdCk7dmFyIHU9UW4oYShlKSxhKHQpLGksbixvLHMpO3JldHVybiBzLmRlbGV0ZShlKSx1O2Nhc2UgTTppZihJcilyZXR1cm4gSXIuY2FsbChlKT09SXIuY2FsbCh0KX1yZXR1cm4hMX0oZSx0LGwscixpLG8scyk7aWYoISgxJnIpKXt2YXIgZD1oJiZCZS5jYWxsKGUsIl9fd3JhcHBlZF9fIiksYj1mJiZCZS5jYWxsKHQsIl9fd3JhcHBlZF9fIik7aWYoZHx8Yil7dmFyIFM9ZD9lLnZhbHVlKCk6ZSxFPWI/dC52YWx1ZSgpOnQ7cmV0dXJuIHN8fChzPW5ldyBHciksbyhTLEUscixpLHMpfX1yZXR1cm4hIV8mJihzfHwocz1uZXcgR3IpLGZ1bmN0aW9uKGUsdCxyLGksbyxzKXt2YXIgYT0xJnIsYz10byhlKSxsPWMubGVuZ3RoO2lmKGwhPXRvKHQpLmxlbmd0aCYmIWEpcmV0dXJuITE7Zm9yKHZhciB1PWw7dS0tOyl7dmFyIGg9Y1t1XTtpZighKGE/aCBpbiB0OkJlLmNhbGwodCxoKSkpcmV0dXJuITF9dmFyIGY9cy5nZXQoZSksXz1zLmdldCh0KTtpZihmJiZfKXJldHVybiBmPT10JiZfPT1lO3ZhciBkPSEwO3Muc2V0KGUsdCkscy5zZXQodCxlKTtmb3IodmFyIHA9YTsrK3U8bDspe3ZhciB2PWVbaD1jW3VdXSxnPXRbaF07aWYoaSl2YXIgeT1hP2koZyx2LGgsdCxlLHMpOmkodixnLGgsZSx0LHMpO2lmKCEoeT09PW4/dj09PWd8fG8odixnLHIsaSxzKTp5KSl7ZD0hMTticmVha31wfHwocD0iY29uc3RydWN0b3IiPT1oKX1pZihkJiYhcCl7dmFyIG09ZS5jb25zdHJ1Y3RvcixiPXQuY29uc3RydWN0b3I7bT09Ynx8ISgiY29uc3RydWN0b3IiaW4gZSl8fCEoImNvbnN0cnVjdG9yImluIHQpfHwiZnVuY3Rpb24iPT10eXBlb2YgbSYmbSBpbnN0YW5jZW9mIG0mJiJmdW5jdGlvbiI9PXR5cGVvZiBiJiZiIGluc3RhbmNlb2YgYnx8KGQ9ITEpfXJldHVybiBzLmRlbGV0ZShlKSxzLmRlbGV0ZSh0KSxkfShlLHQscixpLG8scykpfShlLHQscixpLFJpLG8pKX1mdW5jdGlvbiBUaShlLHQscixpKXt2YXIgbz1yLmxlbmd0aCxzPW8sYT0haTtpZihudWxsPT1lKXJldHVybiFzO2ZvcihlPUxlKGUpO28tLTspe3ZhciBjPXJbb107aWYoYSYmY1syXT9jWzFdIT09ZVtjWzBdXTohKGNbMF1pbiBlKSlyZXR1cm4hMX1mb3IoOysrbzxzOyl7dmFyIGw9KGM9cltvXSlbMF0sdT1lW2xdLGg9Y1sxXTtpZihhJiZjWzJdKXtpZih1PT09biYmIShsIGluIGUpKXJldHVybiExfWVsc2V7dmFyIGY9bmV3IEdyO2lmKGkpdmFyIF89aSh1LGgsbCxlLHQsZik7aWYoIShfPT09bj9SaShoLHUsMyxpLGYpOl8pKXJldHVybiExfX1yZXR1cm4hMH1mdW5jdGlvbiBPaShlKXtyZXR1cm4hKCF0YShlKXx8KHQ9ZSxQZSYmUGUgaW4gdCkpJiYoJHMoZSk/RmU6cGUpLnRlc3QoRm8oZSkpO3ZhciB0fWZ1bmN0aW9uIEJpKGUpe3JldHVybiJmdW5jdGlvbiI9PXR5cGVvZiBlP2U6bnVsbD09ZT9uYzoib2JqZWN0Ij09dHlwZW9mIGU/S3MoZSk/amkoZVswXSxlWzFdKTpIaShlKTpfYyhlKX1mdW5jdGlvbiBEaShlKXtpZighQ28oZSkpcmV0dXJuIHByKGUpO3ZhciB0PVtdO2Zvcih2YXIgciBpbiBMZShlKSlCZS5jYWxsKGUscikmJiJjb25zdHJ1Y3RvciIhPXImJnQucHVzaChyKTtyZXR1cm4gdH1mdW5jdGlvbiBQaShlLHQpe3JldHVybiBlPHR9ZnVuY3Rpb24gSWkoZSx0KXt2YXIgcj0tMSxuPUdzKGUpP2koZS5sZW5ndGgpOltdO3JldHVybiB1aShlLChmdW5jdGlvbihlLGksbyl7blsrK3JdPXQoZSxpLG8pfSkpLG59ZnVuY3Rpb24gSGkoZSl7dmFyIHQ9Y28oZSk7cmV0dXJuIDE9PXQubGVuZ3RoJiZ0WzBdWzJdP0xvKHRbMF1bMF0sdFswXVsxXSk6ZnVuY3Rpb24ocil7cmV0dXJuIHI9PT1lfHxUaShyLGUsdCl9fWZ1bmN0aW9uIGppKGUsdCl7cmV0dXJuIG1vKGUpJiZ3byh0KT9MbyhqbyhlKSx0KTpmdW5jdGlvbihyKXt2YXIgaT1BYShyLGUpO3JldHVybiBpPT09biYmaT09PXQ/a2EocixlKTpSaSh0LGksMyl9fWZ1bmN0aW9uIEZpKGUsdCxyLGksbyl7ZSE9PXQmJnZpKHQsKGZ1bmN0aW9uKHMsYSl7aWYob3x8KG89bmV3IEdyKSx0YShzKSkhZnVuY3Rpb24oZSx0LHIsaSxvLHMsYSl7dmFyIGM9a28oZSxyKSxsPWtvKHQsciksdT1hLmdldChsKTtpZih1KSRyKGUscix1KTtlbHNle3ZhciBoPXM/cyhjLGwscisiIixlLHQsYSk6bixmPWg9PT1uO2lmKGYpe3ZhciBfPUtzKGwpLGQ9IV8mJlhzKGwpLHA9IV8mJiFkJiZ1YShsKTtoPWwsX3x8ZHx8cD9LcyhjKT9oPWM6WXMoYyk/aD1BbihjKTpkPyhmPSExLGg9U24obCwhMCkpOnA/KGY9ITEsaD13bihsLCEwKSk6aD1bXTpvYShsKXx8enMobCk/KGg9Yyx6cyhjKT9oPXlhKGMpOnRhKGMpJiYhJHMoYyl8fChoPXBvKGwpKSk6Zj0hMX1mJiYoYS5zZXQobCxoKSxvKGgsbCxpLHMsYSksYS5kZWxldGUobCkpLCRyKGUscixoKX19KGUsdCxhLHIsRmksaSxvKTtlbHNle3ZhciBjPWk/aShrbyhlLGEpLHMsYSsiIixlLHQsbyk6bjtjPT09biYmKGM9cyksJHIoZSxhLGMpfX0pLEJhKX1mdW5jdGlvbiBXaShlLHQpe3ZhciByPWUubGVuZ3RoO2lmKHIpcmV0dXJuIGdvKHQrPXQ8MD9yOjAscik/ZVt0XTpufWZ1bmN0aW9uIFVpKGUsdCxyKXt0PXQubGVuZ3RoP0V0KHQsKGZ1bmN0aW9uKGUpe3JldHVybiBLcyhlKT9mdW5jdGlvbih0KXtyZXR1cm4gU2kodCwxPT09ZS5sZW5ndGg/ZVswXTplKX06ZX0pKTpbbmNdO3ZhciBpPS0xO3Q9RXQodCxOdChzbygpKSk7dmFyIG49SWkoZSwoZnVuY3Rpb24oZSxyLG4pe3ZhciBvPUV0KHQsKGZ1bmN0aW9uKHQpe3JldHVybiB0KGUpfSkpO3JldHVybntjcml0ZXJpYTpvLGluZGV4OisraSx2YWx1ZTplfX0pKTtyZXR1cm4gZnVuY3Rpb24oZSx0KXt2YXIgaT1lLmxlbmd0aDtmb3IoZS5zb3J0KChmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihlLHQscil7Zm9yKHZhciBpPS0xLG49ZS5jcml0ZXJpYSxvPXQuY3JpdGVyaWEscz1uLmxlbmd0aCxhPXIubGVuZ3RoOysraTxzOyl7dmFyIGM9TG4obltpXSxvW2ldKTtpZihjKXJldHVybiBpPj1hP2M6YyooImRlc2MiPT1yW2ldPy0xOjEpfXJldHVybiBlLmluZGV4LXQuaW5kZXh9KGUsdCxyKX0pKTtpLS07KWVbaV09ZVtpXS52YWx1ZTtyZXR1cm4gZX0obil9ZnVuY3Rpb24gcWkoZSx0LHIpe2Zvcih2YXIgaT0tMSxuPXQubGVuZ3RoLG89e307KytpPG47KXt2YXIgcz10W2ldLGE9U2koZSxzKTtyKGEscykmJlppKG8sZ24ocyxlKSxhKX1yZXR1cm4gb31mdW5jdGlvbiBOaShlLHQscixpKXt2YXIgbj1pP0R0OkJ0LG89LTEscz10Lmxlbmd0aCxhPWU7Zm9yKGU9PT10JiYodD1Bbih0KSksciYmKGE9RXQoZSxOdChyKSkpOysrbzxzOylmb3IodmFyIGM9MCxsPXRbb10sdT1yP3IobCk6bDsoYz1uKGEsdSxjLGkpKT4tMTspYSE9PWUmJml0LmNhbGwoYSxjLDEpLGl0LmNhbGwoZSxjLDEpO3JldHVybiBlfWZ1bmN0aW9uIHppKGUsdCl7Zm9yKHZhciByPWU/dC5sZW5ndGg6MCxpPXItMTtyLS07KXt2YXIgbj10W3JdO2lmKHI9PWl8fG4hPT1vKXt2YXIgbz1uO2dvKG4pP2l0LmNhbGwoZSxuLDEpOmxuKGUsbil9fXJldHVybiBlfWZ1bmN0aW9uIEtpKGUsdCl7cmV0dXJuIGUrdXIoYnIoKSoodC1lKzEpKX1mdW5jdGlvbiBWaShlLHQpe3ZhciByPSIiO2lmKCFlfHx0PDF8fHQ+aClyZXR1cm4gcjtkb3t0JTImJihyKz1lKSwodD11cih0LzIpKSYmKGUrPWUpfXdoaWxlKHQpO3JldHVybiByfWZ1bmN0aW9uIEdpKGUsdCl7cmV0dXJuIFRvKEVvKGUsdCxuYyksZSsiIil9ZnVuY3Rpb24gWWkoZSl7cmV0dXJuIFhyKFVhKGUpKX1mdW5jdGlvbiBYaShlLHQpe3ZhciByPVVhKGUpO3JldHVybiBEbyhyLG9pKHQsMCxyLmxlbmd0aCkpfWZ1bmN0aW9uIFppKGUsdCxyLGkpe2lmKCF0YShlKSlyZXR1cm4gZTtmb3IodmFyIG89LTEscz0odD1nbih0LGUpKS5sZW5ndGgsYT1zLTEsYz1lO251bGwhPWMmJisrbzxzOyl7dmFyIGw9am8odFtvXSksdT1yO2lmKCJfX3Byb3RvX18iPT09bHx8ImNvbnN0cnVjdG9yIj09PWx8fCJwcm90b3R5cGUiPT09bClyZXR1cm4gZTtpZihvIT1hKXt2YXIgaD1jW2xdOyh1PWk/aShoLGwsYyk6bik9PT1uJiYodT10YShoKT9oOmdvKHRbbysxXSk/W106e30pfVFyKGMsbCx1KSxjPWNbbF19cmV0dXJuIGV9dmFyIEppPWtyP2Z1bmN0aW9uKGUsdCl7cmV0dXJuIGtyLnNldChlLHQpLGV9Om5jLCRpPWx0P2Z1bmN0aW9uKGUsdCl7cmV0dXJuIGx0KGUsInRvU3RyaW5nIix7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITEsdmFsdWU6dGModCksd3JpdGFibGU6ITB9KX06bmM7ZnVuY3Rpb24gUWkoZSl7cmV0dXJuIERvKFVhKGUpKX1mdW5jdGlvbiBlbihlLHQscil7dmFyIG49LTEsbz1lLmxlbmd0aDt0PDAmJih0PS10Pm8/MDpvK3QpLChyPXI+bz9vOnIpPDAmJihyKz1vKSxvPXQ+cj8wOnItdD4+PjAsdD4+Pj0wO2Zvcih2YXIgcz1pKG8pOysrbjxvOylzW25dPWVbbit0XTtyZXR1cm4gc31mdW5jdGlvbiB0bihlLHQpe3ZhciByO3JldHVybiB1aShlLChmdW5jdGlvbihlLGksbil7cmV0dXJuIShyPXQoZSxpLG4pKX0pKSwhIXJ9ZnVuY3Rpb24gcm4oZSx0LHIpe3ZhciBpPTAsbj1udWxsPT1lP2k6ZS5sZW5ndGg7aWYoIm51bWJlciI9PXR5cGVvZiB0JiZ0PT10JiZuPD0yMTQ3NDgzNjQ3KXtmb3IoO2k8bjspe3ZhciBvPWkrbj4+PjEscz1lW29dO251bGwhPT1zJiYhbGEocykmJihyP3M8PXQ6czx0KT9pPW8rMTpuPW99cmV0dXJuIG59cmV0dXJuIG5uKGUsdCxuYyxyKX1mdW5jdGlvbiBubihlLHQscixpKXt2YXIgbz0wLHM9bnVsbD09ZT8wOmUubGVuZ3RoO2lmKDA9PT1zKXJldHVybiAwO2Zvcih2YXIgYT0odD1yKHQpKSE9dCxjPW51bGw9PT10LGw9bGEodCksdT10PT09bjtvPHM7KXt2YXIgaD11cigobytzKS8yKSxmPXIoZVtoXSksXz1mIT09bixkPW51bGw9PT1mLHA9Zj09Zix2PWxhKGYpO2lmKGEpdmFyIGc9aXx8cDtlbHNlIGc9dT9wJiYoaXx8Xyk6Yz9wJiZfJiYoaXx8IWQpOmw/cCYmXyYmIWQmJihpfHwhdik6IWQmJiF2JiYoaT9mPD10OmY8dCk7Zz9vPWgrMTpzPWh9cmV0dXJuIGdyKHMsNDI5NDk2NzI5NCl9ZnVuY3Rpb24gb24oZSx0KXtmb3IodmFyIHI9LTEsaT1lLmxlbmd0aCxuPTAsbz1bXTsrK3I8aTspe3ZhciBzPWVbcl0sYT10P3Qocyk6cztpZighcnx8IVVzKGEsYykpe3ZhciBjPWE7b1tuKytdPTA9PT1zPzA6c319cmV0dXJuIG99ZnVuY3Rpb24gc24oZSl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlP2U6bGEoZSk/ZjorZX1mdW5jdGlvbiBhbihlKXtpZigic3RyaW5nIj09dHlwZW9mIGUpcmV0dXJuIGU7aWYoS3MoZSkpcmV0dXJuIEV0KGUsYW4pKyIiO2lmKGxhKGUpKXJldHVybiBIcj9Ici5jYWxsKGUpOiIiO3ZhciB0PWUrIiI7cmV0dXJuIjAiPT10JiYxL2U9PS0xLzA/Ii0wIjp0fWZ1bmN0aW9uIGNuKGUsdCxyKXt2YXIgaT0tMSxuPXd0LG89ZS5sZW5ndGgscz0hMCxhPVtdLGM9YTtpZihyKXM9ITEsbj1MdDtlbHNlIGlmKG8+PTIwMCl7dmFyIGw9dD9udWxsOkduKGUpO2lmKGwpcmV0dXJuIHJyKGwpO3M9ITEsbj1LdCxjPW5ldyBWcn1lbHNlIGM9dD9bXTphO2U6Zm9yKDsrK2k8bzspe3ZhciB1PWVbaV0saD10P3QodSk6dTtpZih1PXJ8fDAhPT11P3U6MCxzJiZoPT1oKXtmb3IodmFyIGY9Yy5sZW5ndGg7Zi0tOylpZihjW2ZdPT09aCljb250aW51ZSBlO3QmJmMucHVzaChoKSxhLnB1c2godSl9ZWxzZSBuKGMsaCxyKXx8KGMhPT1hJiZjLnB1c2goaCksYS5wdXNoKHUpKX1yZXR1cm4gYX1mdW5jdGlvbiBsbihlLHQpe3JldHVybiBudWxsPT0oZT14byhlLHQ9Z24odCxlKSkpfHxkZWxldGUgZVtqbyhKbyh0KSldfWZ1bmN0aW9uIHVuKGUsdCxyLGkpe3JldHVybiBaaShlLHQscihTaShlLHQpKSxpKX1mdW5jdGlvbiBobihlLHQscixpKXtmb3IodmFyIG49ZS5sZW5ndGgsbz1pP246LTE7KGk/by0tOisrbzxuKSYmdChlW29dLG8sZSk7KTtyZXR1cm4gcj9lbihlLGk/MDpvLGk/bysxOm4pOmVuKGUsaT9vKzE6MCxpP246byl9ZnVuY3Rpb24gZm4oZSx0KXt2YXIgcj1lO3JldHVybiByIGluc3RhbmNlb2YgcXImJihyPXIudmFsdWUoKSksQXQodCwoZnVuY3Rpb24oZSx0KXtyZXR1cm4gdC5mdW5jLmFwcGx5KHQudGhpc0FyZyx4dChbZV0sdC5hcmdzKSl9KSxyKX1mdW5jdGlvbiBfbihlLHQscil7dmFyIG49ZS5sZW5ndGg7aWYobjwyKXJldHVybiBuP2NuKGVbMF0pOltdO2Zvcih2YXIgbz0tMSxzPWkobik7KytvPG47KWZvcih2YXIgYT1lW29dLGM9LTE7KytjPG47KWMhPW8mJihzW29dPWxpKHNbb118fGEsZVtjXSx0LHIpKTtyZXR1cm4gY24ocGkocywxKSx0LHIpfWZ1bmN0aW9uIGRuKGUsdCxyKXtmb3IodmFyIGk9LTEsbz1lLmxlbmd0aCxzPXQubGVuZ3RoLGE9e307KytpPG87KXt2YXIgYz1pPHM/dFtpXTpuO3IoYSxlW2ldLGMpfXJldHVybiBhfWZ1bmN0aW9uIHBuKGUpe3JldHVybiBZcyhlKT9lOltdfWZ1bmN0aW9uIHZuKGUpe3JldHVybiJmdW5jdGlvbiI9PXR5cGVvZiBlP2U6bmN9ZnVuY3Rpb24gZ24oZSx0KXtyZXR1cm4gS3MoZSk/ZTptbyhlLHQpP1tlXTpIbyhtYShlKSl9dmFyIHluPUdpO2Z1bmN0aW9uIG1uKGUsdCxyKXt2YXIgaT1lLmxlbmd0aDtyZXR1cm4gcj1yPT09bj9pOnIsIXQmJnI+PWk/ZTplbihlLHQscil9dmFyIGJuPXV0fHxmdW5jdGlvbihlKXtyZXR1cm4gb3QuY2xlYXJUaW1lb3V0KGUpfTtmdW5jdGlvbiBTbihlLHQpe2lmKHQpcmV0dXJuIGUuc2xpY2UoKTt2YXIgcj1lLmxlbmd0aCxpPU5lP05lKHIpOm5ldyBlLmNvbnN0cnVjdG9yKHIpO3JldHVybiBlLmNvcHkoaSksaX1mdW5jdGlvbiBDbihlKXt2YXIgdD1uZXcgZS5jb25zdHJ1Y3RvcihlLmJ5dGVMZW5ndGgpO3JldHVybiBuZXcgcWUodCkuc2V0KG5ldyBxZShlKSksdH1mdW5jdGlvbiB3bihlLHQpe3ZhciByPXQ/Q24oZS5idWZmZXIpOmUuYnVmZmVyO3JldHVybiBuZXcgZS5jb25zdHJ1Y3RvcihyLGUuYnl0ZU9mZnNldCxlLmxlbmd0aCl9ZnVuY3Rpb24gTG4oZSx0KXtpZihlIT09dCl7dmFyIHI9ZSE9PW4saT1udWxsPT09ZSxvPWU9PWUscz1sYShlKSxhPXQhPT1uLGM9bnVsbD09PXQsbD10PT10LHU9bGEodCk7aWYoIWMmJiF1JiYhcyYmZT50fHxzJiZhJiZsJiYhYyYmIXV8fGkmJmEmJmx8fCFyJiZsfHwhbylyZXR1cm4gMTtpZighaSYmIXMmJiF1JiZlPHR8fHUmJnImJm8mJiFpJiYhc3x8YyYmciYmb3x8IWEmJm98fCFsKXJldHVybi0xfXJldHVybiAwfWZ1bmN0aW9uIEVuKGUsdCxyLG4pe2Zvcih2YXIgbz0tMSxzPWUubGVuZ3RoLGE9ci5sZW5ndGgsYz0tMSxsPXQubGVuZ3RoLHU9dnIocy1hLDApLGg9aShsK3UpLGY9IW47KytjPGw7KWhbY109dFtjXTtmb3IoOysrbzxhOykoZnx8bzxzKSYmKGhbcltvXV09ZVtvXSk7Zm9yKDt1LS07KWhbYysrXT1lW28rK107cmV0dXJuIGh9ZnVuY3Rpb24geG4oZSx0LHIsbil7Zm9yKHZhciBvPS0xLHM9ZS5sZW5ndGgsYT0tMSxjPXIubGVuZ3RoLGw9LTEsdT10Lmxlbmd0aCxoPXZyKHMtYywwKSxmPWkoaCt1KSxfPSFuOysrbzxoOylmW29dPWVbb107Zm9yKHZhciBkPW87KytsPHU7KWZbZCtsXT10W2xdO2Zvcig7KythPGM7KShffHxvPHMpJiYoZltkK3JbYV1dPWVbbysrXSk7cmV0dXJuIGZ9ZnVuY3Rpb24gQW4oZSx0KXt2YXIgcj0tMSxuPWUubGVuZ3RoO2Zvcih0fHwodD1pKG4pKTsrK3I8bjspdFtyXT1lW3JdO3JldHVybiB0fWZ1bmN0aW9uIGtuKGUsdCxyLGkpe3ZhciBvPSFyO3J8fChyPXt9KTtmb3IodmFyIHM9LTEsYT10Lmxlbmd0aDsrK3M8YTspe3ZhciBjPXRbc10sbD1pP2kocltjXSxlW2NdLGMscixlKTpuO2w9PT1uJiYobD1lW2NdKSxvP2lpKHIsYyxsKTpRcihyLGMsbCl9cmV0dXJuIHJ9ZnVuY3Rpb24gTW4oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt2YXIgbj1LcyhyKT95dDp0aSxvPXQ/dCgpOnt9O3JldHVybiBuKHIsZSxzbyhpLDIpLG8pfX1mdW5jdGlvbiBSbihlKXtyZXR1cm4gR2koKGZ1bmN0aW9uKHQscil7dmFyIGk9LTEsbz1yLmxlbmd0aCxzPW8+MT9yW28tMV06bixhPW8+Mj9yWzJdOm47Zm9yKHM9ZS5sZW5ndGg+MyYmImZ1bmN0aW9uIj09dHlwZW9mIHM/KG8tLSxzKTpuLGEmJnlvKHJbMF0sclsxXSxhKSYmKHM9bzwzP246cyxvPTEpLHQ9TGUodCk7KytpPG87KXt2YXIgYz1yW2ldO2MmJmUodCxjLGkscyl9cmV0dXJuIHR9KSl9ZnVuY3Rpb24gVG4oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXtpZihudWxsPT1yKXJldHVybiByO2lmKCFHcyhyKSlyZXR1cm4gZShyLGkpO2Zvcih2YXIgbj1yLmxlbmd0aCxvPXQ/bjotMSxzPUxlKHIpOyh0P28tLTorK288bikmJiExIT09aShzW29dLG8scyk7KTtyZXR1cm4gcn19ZnVuY3Rpb24gT24oZSl7cmV0dXJuIGZ1bmN0aW9uKHQscixpKXtmb3IodmFyIG49LTEsbz1MZSh0KSxzPWkodCksYT1zLmxlbmd0aDthLS07KXt2YXIgYz1zW2U/YTorK25dO2lmKCExPT09cihvW2NdLGMsbykpYnJlYWt9cmV0dXJuIHR9fWZ1bmN0aW9uIEJuKGUpe3JldHVybiBmdW5jdGlvbih0KXt2YXIgcj0kdCh0PW1hKHQpKT9vcih0KTpuLGk9cj9yWzBdOnQuY2hhckF0KDApLG89cj9tbihyLDEpLmpvaW4oIiIpOnQuc2xpY2UoMSk7cmV0dXJuIGlbZV0oKStvfX1mdW5jdGlvbiBEbihlKXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIEF0KCRhKHphKHQpLnJlcGxhY2UoemUsIiIpKSxlLCIiKX19ZnVuY3Rpb24gUG4oZSl7cmV0dXJuIGZ1bmN0aW9uKCl7dmFyIHQ9YXJndW1lbnRzO3N3aXRjaCh0Lmxlbmd0aCl7Y2FzZSAwOnJldHVybiBuZXcgZTtjYXNlIDE6cmV0dXJuIG5ldyBlKHRbMF0pO2Nhc2UgMjpyZXR1cm4gbmV3IGUodFswXSx0WzFdKTtjYXNlIDM6cmV0dXJuIG5ldyBlKHRbMF0sdFsxXSx0WzJdKTtjYXNlIDQ6cmV0dXJuIG5ldyBlKHRbMF0sdFsxXSx0WzJdLHRbM10pO2Nhc2UgNTpyZXR1cm4gbmV3IGUodFswXSx0WzFdLHRbMl0sdFszXSx0WzRdKTtjYXNlIDY6cmV0dXJuIG5ldyBlKHRbMF0sdFsxXSx0WzJdLHRbM10sdFs0XSx0WzVdKTtjYXNlIDc6cmV0dXJuIG5ldyBlKHRbMF0sdFsxXSx0WzJdLHRbM10sdFs0XSx0WzVdLHRbNl0pfXZhciByPUZyKGUucHJvdG90eXBlKSxpPWUuYXBwbHkocix0KTtyZXR1cm4gdGEoaSk/aTpyfX1mdW5jdGlvbiBJbihlKXtyZXR1cm4gZnVuY3Rpb24odCxyLGkpe3ZhciBvPUxlKHQpO2lmKCFHcyh0KSl7dmFyIHM9c28ociwzKTt0PU9hKHQpLHI9ZnVuY3Rpb24oZSl7cmV0dXJuIHMob1tlXSxlLG8pfX12YXIgYT1lKHQscixpKTtyZXR1cm4gYT4tMT9vW3M/dFthXTphXTpufX1mdW5jdGlvbiBIbihlKXtyZXR1cm4gZW8oKGZ1bmN0aW9uKHQpe3ZhciByPXQubGVuZ3RoLGk9cixzPVVyLnByb3RvdHlwZS50aHJ1O2ZvcihlJiZ0LnJldmVyc2UoKTtpLS07KXt2YXIgYT10W2ldO2lmKCJmdW5jdGlvbiIhPXR5cGVvZiBhKXRocm93IG5ldyBBZShvKTtpZihzJiYhYyYmIndyYXBwZXIiPT1ubyhhKSl2YXIgYz1uZXcgVXIoW10sITApfWZvcihpPWM/aTpyOysraTxyOyl7dmFyIGw9bm8oYT10W2ldKSx1PSJ3cmFwcGVyIj09bD9pbyhhKTpuO2M9dSYmYm8odVswXSkmJjQyND09dVsxXSYmIXVbNF0ubGVuZ3RoJiYxPT11WzldP2Nbbm8odVswXSldLmFwcGx5KGMsdVszXSk6MT09YS5sZW5ndGgmJmJvKGEpP2NbbF0oKTpjLnRocnUoYSl9cmV0dXJuIGZ1bmN0aW9uKCl7dmFyIGU9YXJndW1lbnRzLGk9ZVswXTtpZihjJiYxPT1lLmxlbmd0aCYmS3MoaSkpcmV0dXJuIGMucGxhbnQoaSkudmFsdWUoKTtmb3IodmFyIG49MCxvPXI/dFtuXS5hcHBseSh0aGlzLGUpOmk7KytuPHI7KW89dFtuXS5jYWxsKHRoaXMsbyk7cmV0dXJuIG99fSkpfWZ1bmN0aW9uIGpuKGUsdCxyLG8scyxhLGMsdSxoLGYpe3ZhciBfPXQmbCxkPTEmdCxwPTImdCx2PTI0JnQsZz01MTImdCx5PXA/bjpQbihlKTtyZXR1cm4gZnVuY3Rpb24gbigpe2Zvcih2YXIgbD1hcmd1bWVudHMubGVuZ3RoLG09aShsKSxiPWw7Yi0tOyltW2JdPWFyZ3VtZW50c1tiXTtpZih2KXZhciBTPW9vKG4pLEM9WXQobSxTKTtpZihvJiYobT1FbihtLG8scyx2KSksYSYmKG09eG4obSxhLGMsdikpLGwtPUMsdiYmbDxmKXt2YXIgdz10cihtLFMpO3JldHVybiBLbihlLHQsam4sbi5wbGFjZWhvbGRlcixyLG0sdyx1LGgsZi1sKX12YXIgTD1kP3I6dGhpcyxFPXA/TFtlXTplO3JldHVybiBsPW0ubGVuZ3RoLHU/bT1BbyhtLHUpOmcmJmw+MSYmbS5yZXZlcnNlKCksXyYmaDxsJiYobS5sZW5ndGg9aCksdGhpcyYmdGhpcyE9PW90JiZ0aGlzIGluc3RhbmNlb2YgbiYmKEU9eXx8UG4oRSkpLEUuYXBwbHkoTCxtKX19ZnVuY3Rpb24gRm4oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXtyZXR1cm4gZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuIHlpKGUsKGZ1bmN0aW9uKGUsbixvKXt0KGkscihlKSxuLG8pfSkpLGl9KHIsZSx0KGkpLHt9KX19ZnVuY3Rpb24gV24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt2YXIgbztpZihyPT09biYmaT09PW4pcmV0dXJuIHQ7aWYociE9PW4mJihvPXIpLGkhPT1uKXtpZihvPT09bilyZXR1cm4gaTsic3RyaW5nIj09dHlwZW9mIHJ8fCJzdHJpbmciPT10eXBlb2YgaT8ocj1hbihyKSxpPWFuKGkpKToocj1zbihyKSxpPXNuKGkpKSxvPWUocixpKX1yZXR1cm4gb319ZnVuY3Rpb24gVW4oZSl7cmV0dXJuIGVvKChmdW5jdGlvbih0KXtyZXR1cm4gdD1FdCh0LE50KHNvKCkpKSxHaSgoZnVuY3Rpb24ocil7dmFyIGk9dGhpcztyZXR1cm4gZSh0LChmdW5jdGlvbihlKXtyZXR1cm4gZ3QoZSxpLHIpfSkpfSkpfSkpfWZ1bmN0aW9uIHFuKGUsdCl7dmFyIHI9KHQ9dD09PW4/IiAiOmFuKHQpKS5sZW5ndGg7aWYocjwyKXJldHVybiByP1ZpKHQsZSk6dDt2YXIgaT1WaSh0LGxyKGUvbnIodCkpKTtyZXR1cm4gJHQodCk/bW4ob3IoaSksMCxlKS5qb2luKCIiKTppLnNsaWNlKDAsZSl9ZnVuY3Rpb24gTm4oZSl7cmV0dXJuIGZ1bmN0aW9uKHQscixvKXtyZXR1cm4gbyYmIm51bWJlciIhPXR5cGVvZiBvJiZ5byh0LHIsbykmJihyPW89biksdD1kYSh0KSxyPT09bj8ocj10LHQ9MCk6cj1kYShyKSxmdW5jdGlvbihlLHQscixuKXtmb3IodmFyIG89LTEscz12cihscigodC1lKS8ocnx8MSkpLDApLGE9aShzKTtzLS07KWFbbj9zOisrb109ZSxlKz1yO3JldHVybiBhfSh0LHIsbz1vPT09bj90PHI/MTotMTpkYShvKSxlKX19ZnVuY3Rpb24gem4oZSl7cmV0dXJuIGZ1bmN0aW9uKHQscil7cmV0dXJuInN0cmluZyI9PXR5cGVvZiB0JiYic3RyaW5nIj09dHlwZW9mIHJ8fCh0PWdhKHQpLHI9Z2EocikpLGUodCxyKX19ZnVuY3Rpb24gS24oZSx0LHIsaSxvLHMsYSxsLHUsaCl7dmFyIGY9OCZ0O3R8PWY/Yzo2NCw0Jih0Jj1+KGY/NjQ6YykpfHwodCY9LTQpO3ZhciBfPVtlLHQsbyxmP3M6bixmP2E6bixmP246cyxmP246YSxsLHUsaF0sZD1yLmFwcGx5KG4sXyk7cmV0dXJuIGJvKGUpJiZNbyhkLF8pLGQucGxhY2Vob2xkZXI9aSxPbyhkLGUsdCl9ZnVuY3Rpb24gVm4oZSl7dmFyIHQ9d2VbZV07cmV0dXJuIGZ1bmN0aW9uKGUscil7aWYoZT1nYShlKSwocj1udWxsPT1yPzA6Z3IocGEociksMjkyKSkmJl9yKGUpKXt2YXIgaT0obWEoZSkrImUiKS5zcGxpdCgiZSIpO3JldHVybisoKGk9KG1hKHQoaVswXSsiZSIrKCtpWzFdK3IpKSkrImUiKS5zcGxpdCgiZSIpKVswXSsiZSIrKCtpWzFdLXIpKX1yZXR1cm4gdChlKX19dmFyIEduPUVyJiYxL3JyKG5ldyBFcihbLC0wXSkpWzFdPT11P2Z1bmN0aW9uKGUpe3JldHVybiBuZXcgRXIoZSl9OmxjO2Z1bmN0aW9uIFluKGUpe3JldHVybiBmdW5jdGlvbih0KXt2YXIgcj1mbyh0KTtyZXR1cm4gcj09Qz9RdCh0KTpyPT1BP2lyKHQpOmZ1bmN0aW9uKGUsdCl7cmV0dXJuIEV0KHQsKGZ1bmN0aW9uKHQpe3JldHVyblt0LGVbdF1dfSkpfSh0LGUodCkpfX1mdW5jdGlvbiBYbihlLHQscixzLHUsaCxmLF8pe3ZhciBkPTImdDtpZighZCYmImZ1bmN0aW9uIiE9dHlwZW9mIGUpdGhyb3cgbmV3IEFlKG8pO3ZhciBwPXM/cy5sZW5ndGg6MDtpZihwfHwodCY9LTk3LHM9dT1uKSxmPWY9PT1uP2Y6dnIocGEoZiksMCksXz1fPT09bj9fOnBhKF8pLHAtPXU/dS5sZW5ndGg6MCw2NCZ0KXt2YXIgdj1zLGc9dTtzPXU9bn12YXIgeT1kP246aW8oZSksbT1bZSx0LHIscyx1LHYsZyxoLGYsX107aWYoeSYmZnVuY3Rpb24oZSx0KXt2YXIgcj1lWzFdLGk9dFsxXSxuPXJ8aSxvPW48MTMxLHM9aT09bCYmOD09cnx8aT09bCYmMjU2PT1yJiZlWzddLmxlbmd0aDw9dFs4XXx8Mzg0PT1pJiZ0WzddLmxlbmd0aDw9dFs4XSYmOD09cjtpZighbyYmIXMpcmV0dXJuIGU7MSZpJiYoZVsyXT10WzJdLG58PTEmcj8wOjQpO3ZhciBjPXRbM107aWYoYyl7dmFyIHU9ZVszXTtlWzNdPXU/RW4odSxjLHRbNF0pOmMsZVs0XT11P3RyKGVbM10sYSk6dFs0XX0oYz10WzVdKSYmKHU9ZVs1XSxlWzVdPXU/eG4odSxjLHRbNl0pOmMsZVs2XT11P3RyKGVbNV0sYSk6dFs2XSksKGM9dFs3XSkmJihlWzddPWMpLGkmbCYmKGVbOF09bnVsbD09ZVs4XT90WzhdOmdyKGVbOF0sdFs4XSkpLG51bGw9PWVbOV0mJihlWzldPXRbOV0pLGVbMF09dFswXSxlWzFdPW59KG0seSksZT1tWzBdLHQ9bVsxXSxyPW1bMl0scz1tWzNdLHU9bVs0XSwhKF89bVs5XT1tWzldPT09bj9kPzA6ZS5sZW5ndGg6dnIobVs5XS1wLDApKSYmMjQmdCYmKHQmPS0yNSksdCYmMSE9dCliPTg9PXR8fDE2PT10P2Z1bmN0aW9uKGUsdCxyKXt2YXIgbz1QbihlKTtyZXR1cm4gZnVuY3Rpb24gcygpe2Zvcih2YXIgYT1hcmd1bWVudHMubGVuZ3RoLGM9aShhKSxsPWEsdT1vbyhzKTtsLS07KWNbbF09YXJndW1lbnRzW2xdO3ZhciBoPWE8MyYmY1swXSE9PXUmJmNbYS0xXSE9PXU/W106dHIoYyx1KTtyZXR1cm4oYS09aC5sZW5ndGgpPHI/S24oZSx0LGpuLHMucGxhY2Vob2xkZXIsbixjLGgsbixuLHItYSk6Z3QodGhpcyYmdGhpcyE9PW90JiZ0aGlzIGluc3RhbmNlb2Ygcz9vOmUsdGhpcyxjKX19KGUsdCxfKTp0IT1jJiYzMyE9dHx8dS5sZW5ndGg/am4uYXBwbHkobixtKTpmdW5jdGlvbihlLHQscixuKXt2YXIgbz0xJnQscz1QbihlKTtyZXR1cm4gZnVuY3Rpb24gdCgpe2Zvcih2YXIgYT0tMSxjPWFyZ3VtZW50cy5sZW5ndGgsbD0tMSx1PW4ubGVuZ3RoLGg9aSh1K2MpLGY9dGhpcyYmdGhpcyE9PW90JiZ0aGlzIGluc3RhbmNlb2YgdD9zOmU7KytsPHU7KWhbbF09bltsXTtmb3IoO2MtLTspaFtsKytdPWFyZ3VtZW50c1srK2FdO3JldHVybiBndChmLG8/cjp0aGlzLGgpfX0oZSx0LHIscyk7ZWxzZSB2YXIgYj1mdW5jdGlvbihlLHQscil7dmFyIGk9MSZ0LG49UG4oZSk7cmV0dXJuIGZ1bmN0aW9uIHQoKXtyZXR1cm4odGhpcyYmdGhpcyE9PW90JiZ0aGlzIGluc3RhbmNlb2YgdD9uOmUpLmFwcGx5KGk/cjp0aGlzLGFyZ3VtZW50cyl9fShlLHQscik7cmV0dXJuIE9vKCh5P0ppOk1vKShiLG0pLGUsdCl9ZnVuY3Rpb24gWm4oZSx0LHIsaSl7cmV0dXJuIGU9PT1ufHxVcyhlLFJlW3JdKSYmIUJlLmNhbGwoaSxyKT90OmV9ZnVuY3Rpb24gSm4oZSx0LHIsaSxvLHMpe3JldHVybiB0YShlKSYmdGEodCkmJihzLnNldCh0LGUpLEZpKGUsdCxuLEpuLHMpLHMuZGVsZXRlKHQpKSxlfWZ1bmN0aW9uICRuKGUpe3JldHVybiBvYShlKT9uOmV9ZnVuY3Rpb24gUW4oZSx0LHIsaSxvLHMpe3ZhciBhPTEmcixjPWUubGVuZ3RoLGw9dC5sZW5ndGg7aWYoYyE9bCYmIShhJiZsPmMpKXJldHVybiExO3ZhciB1PXMuZ2V0KGUpLGg9cy5nZXQodCk7aWYodSYmaClyZXR1cm4gdT09dCYmaD09ZTt2YXIgZj0tMSxfPSEwLGQ9MiZyP25ldyBWcjpuO2ZvcihzLnNldChlLHQpLHMuc2V0KHQsZSk7KytmPGM7KXt2YXIgcD1lW2ZdLHY9dFtmXTtpZihpKXZhciBnPWE/aSh2LHAsZix0LGUscyk6aShwLHYsZixlLHQscyk7aWYoZyE9PW4pe2lmKGcpY29udGludWU7Xz0hMTticmVha31pZihkKXtpZighTXQodCwoZnVuY3Rpb24oZSx0KXtpZighS3QoZCx0KSYmKHA9PT1lfHxvKHAsZSxyLGkscykpKXJldHVybiBkLnB1c2godCl9KSkpe189ITE7YnJlYWt9fWVsc2UgaWYocCE9PXYmJiFvKHAsdixyLGkscykpe189ITE7YnJlYWt9fXJldHVybiBzLmRlbGV0ZShlKSxzLmRlbGV0ZSh0KSxffWZ1bmN0aW9uIGVvKGUpe3JldHVybiBUbyhFbyhlLG4sVm8pLGUrIiIpfWZ1bmN0aW9uIHRvKGUpe3JldHVybiBDaShlLE9hLHVvKX1mdW5jdGlvbiBybyhlKXtyZXR1cm4gQ2koZSxCYSxobyl9dmFyIGlvPWtyP2Z1bmN0aW9uKGUpe3JldHVybiBrci5nZXQoZSl9OmxjO2Z1bmN0aW9uIG5vKGUpe2Zvcih2YXIgdD1lLm5hbWUrIiIscj1Nclt0XSxpPUJlLmNhbGwoTXIsdCk/ci5sZW5ndGg6MDtpLS07KXt2YXIgbj1yW2ldLG89bi5mdW5jO2lmKG51bGw9PW98fG89PWUpcmV0dXJuIG4ubmFtZX1yZXR1cm4gdH1mdW5jdGlvbiBvbyhlKXtyZXR1cm4oQmUuY2FsbChqciwicGxhY2Vob2xkZXIiKT9qcjplKS5wbGFjZWhvbGRlcn1mdW5jdGlvbiBzbygpe3ZhciBlPWpyLml0ZXJhdGVlfHxvYztyZXR1cm4gZT1lPT09b2M/Qmk6ZSxhcmd1bWVudHMubGVuZ3RoP2UoYXJndW1lbnRzWzBdLGFyZ3VtZW50c1sxXSk6ZX1mdW5jdGlvbiBhbyhlLHQpe3ZhciByLGksbj1lLl9fZGF0YV9fO3JldHVybigic3RyaW5nIj09KGk9dHlwZW9mKHI9dCkpfHwibnVtYmVyIj09aXx8InN5bWJvbCI9PWl8fCJib29sZWFuIj09aT8iX19wcm90b19fIiE9PXI6bnVsbD09PXIpP25bInN0cmluZyI9PXR5cGVvZiB0PyJzdHJpbmciOiJoYXNoIl06bi5tYXB9ZnVuY3Rpb24gY28oZSl7Zm9yKHZhciB0PU9hKGUpLHI9dC5sZW5ndGg7ci0tOyl7dmFyIGk9dFtyXSxuPWVbaV07dFtyXT1baSxuLHdvKG4pXX1yZXR1cm4gdH1mdW5jdGlvbiBsbyhlLHQpe3ZhciByPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIG51bGw9PWU/bjplW3RdfShlLHQpO3JldHVybiBPaShyKT9yOm59dmFyIHVvPWhyP2Z1bmN0aW9uKGUpe3JldHVybiBudWxsPT1lP1tdOihlPUxlKGUpLEN0KGhyKGUpLChmdW5jdGlvbih0KXtyZXR1cm4gZXQuY2FsbChlLHQpfSkpKX06dmMsaG89aHI/ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PVtdO2U7KXh0KHQsdW8oZSkpLGU9VmUoZSk7cmV0dXJuIHR9OnZjLGZvPXdpO2Z1bmN0aW9uIF9vKGUsdCxyKXtmb3IodmFyIGk9LTEsbj0odD1nbih0LGUpKS5sZW5ndGgsbz0hMTsrK2k8bjspe3ZhciBzPWpvKHRbaV0pO2lmKCEobz1udWxsIT1lJiZyKGUscykpKWJyZWFrO2U9ZVtzXX1yZXR1cm4gb3x8KytpIT1uP286ISEobj1udWxsPT1lPzA6ZS5sZW5ndGgpJiZlYShuKSYmZ28ocyxuKSYmKEtzKGUpfHx6cyhlKSl9ZnVuY3Rpb24gcG8oZSl7cmV0dXJuImZ1bmN0aW9uIiE9dHlwZW9mIGUuY29uc3RydWN0b3J8fENvKGUpP3t9OkZyKFZlKGUpKX1mdW5jdGlvbiB2byhlKXtyZXR1cm4gS3MoZSl8fHpzKGUpfHwhIShudCYmZSYmZVtudF0pfWZ1bmN0aW9uIGdvKGUsdCl7dmFyIHI9dHlwZW9mIGU7cmV0dXJuISEodD1udWxsPT10P2g6dCkmJigibnVtYmVyIj09cnx8InN5bWJvbCIhPXImJmdlLnRlc3QoZSkpJiZlPi0xJiZlJTE9PTAmJmU8dH1mdW5jdGlvbiB5byhlLHQscil7aWYoIXRhKHIpKXJldHVybiExO3ZhciBpPXR5cGVvZiB0O3JldHVybiEhKCJudW1iZXIiPT1pP0dzKHIpJiZnbyh0LHIubGVuZ3RoKToic3RyaW5nIj09aSYmdCBpbiByKSYmVXMoclt0XSxlKX1mdW5jdGlvbiBtbyhlLHQpe2lmKEtzKGUpKXJldHVybiExO3ZhciByPXR5cGVvZiBlO3JldHVybiEoIm51bWJlciIhPXImJiJzeW1ib2wiIT1yJiYiYm9vbGVhbiIhPXImJm51bGwhPWUmJiFsYShlKSl8fFEudGVzdChlKXx8ISQudGVzdChlKXx8bnVsbCE9dCYmZSBpbiBMZSh0KX1mdW5jdGlvbiBibyhlKXt2YXIgdD1ubyhlKSxyPWpyW3RdO2lmKCJmdW5jdGlvbiIhPXR5cGVvZiByfHwhKHQgaW4gcXIucHJvdG90eXBlKSlyZXR1cm4hMTtpZihlPT09cilyZXR1cm4hMDt2YXIgaT1pbyhyKTtyZXR1cm4hIWkmJmU9PT1pWzBdfShDciYmZm8obmV3IENyKG5ldyBBcnJheUJ1ZmZlcigxKSkpIT1PfHx3ciYmZm8obmV3IHdyKSE9Q3x8THImJmZvKExyLnJlc29sdmUoKSkhPUV8fEVyJiZmbyhuZXcgRXIpIT1BfHx4ciYmZm8obmV3IHhyKSE9UikmJihmbz1mdW5jdGlvbihlKXt2YXIgdD13aShlKSxyPXQ9PUw/ZS5jb25zdHJ1Y3RvcjpuLGk9cj9GbyhyKToiIjtpZihpKXN3aXRjaChpKXtjYXNlIFJyOnJldHVybiBPO2Nhc2UgVHI6cmV0dXJuIEM7Y2FzZSBPcjpyZXR1cm4gRTtjYXNlIEJyOnJldHVybiBBO2Nhc2UgRHI6cmV0dXJuIFJ9cmV0dXJuIHR9KTt2YXIgU289VGU/JHM6Z2M7ZnVuY3Rpb24gQ28oZSl7dmFyIHQ9ZSYmZS5jb25zdHJ1Y3RvcjtyZXR1cm4gZT09PSgiZnVuY3Rpb24iPT10eXBlb2YgdCYmdC5wcm90b3R5cGV8fFJlKX1mdW5jdGlvbiB3byhlKXtyZXR1cm4gZT09ZSYmIXRhKGUpfWZ1bmN0aW9uIExvKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIpe3JldHVybiBudWxsIT1yJiZyW2VdPT09dCYmKHQhPT1ufHxlIGluIExlKHIpKX19ZnVuY3Rpb24gRW8oZSx0LHIpe3JldHVybiB0PXZyKHQ9PT1uP2UubGVuZ3RoLTE6dCwwKSxmdW5jdGlvbigpe2Zvcih2YXIgbj1hcmd1bWVudHMsbz0tMSxzPXZyKG4ubGVuZ3RoLXQsMCksYT1pKHMpOysrbzxzOylhW29dPW5bdCtvXTtvPS0xO2Zvcih2YXIgYz1pKHQrMSk7KytvPHQ7KWNbb109bltvXTtyZXR1cm4gY1t0XT1yKGEpLGd0KGUsdGhpcyxjKX19ZnVuY3Rpb24geG8oZSx0KXtyZXR1cm4gdC5sZW5ndGg8Mj9lOlNpKGUsZW4odCwwLC0xKSl9ZnVuY3Rpb24gQW8oZSx0KXtmb3IodmFyIHI9ZS5sZW5ndGgsaT1ncih0Lmxlbmd0aCxyKSxvPUFuKGUpO2ktLTspe3ZhciBzPXRbaV07ZVtpXT1nbyhzLHIpP29bc106bn1yZXR1cm4gZX1mdW5jdGlvbiBrbyhlLHQpe2lmKCgiY29uc3RydWN0b3IiIT09dHx8ImZ1bmN0aW9uIiE9dHlwZW9mIGVbdF0pJiYiX19wcm90b19fIiE9dClyZXR1cm4gZVt0XX12YXIgTW89Qm8oSmkpLFJvPWp0fHxmdW5jdGlvbihlLHQpe3JldHVybiBvdC5zZXRUaW1lb3V0KGUsdCl9LFRvPUJvKCRpKTtmdW5jdGlvbiBPbyhlLHQscil7dmFyIGk9dCsiIjtyZXR1cm4gVG8oZSxmdW5jdGlvbihlLHQpe3ZhciByPXQubGVuZ3RoO2lmKCFyKXJldHVybiBlO3ZhciBpPXItMTtyZXR1cm4gdFtpXT0ocj4xPyImICI6IiIpK3RbaV0sdD10LmpvaW4ocj4yPyIsICI6IiAiKSxlLnJlcGxhY2Uob2UsIntcbi8qIFt3cmFwcGVkIHdpdGggIit0KyJdICovXG4iKX0oaSxmdW5jdGlvbihlLHQpe3JldHVybiBtdChkLChmdW5jdGlvbihyKXt2YXIgaT0iXy4iK3JbMF07dCZyWzFdJiYhd3QoZSxpKSYmZS5wdXNoKGkpfSkpLGUuc29ydCgpfShmdW5jdGlvbihlKXt2YXIgdD1lLm1hdGNoKHNlKTtyZXR1cm4gdD90WzFdLnNwbGl0KGFlKTpbXX0oaSkscikpKX1mdW5jdGlvbiBCbyhlKXt2YXIgdD0wLHI9MDtyZXR1cm4gZnVuY3Rpb24oKXt2YXIgaT15cigpLG89MTYtKGktcik7aWYocj1pLG8+MCl7aWYoKyt0Pj04MDApcmV0dXJuIGFyZ3VtZW50c1swXX1lbHNlIHQ9MDtyZXR1cm4gZS5hcHBseShuLGFyZ3VtZW50cyl9fWZ1bmN0aW9uIERvKGUsdCl7dmFyIHI9LTEsaT1lLmxlbmd0aCxvPWktMTtmb3IodD10PT09bj9pOnQ7KytyPHQ7KXt2YXIgcz1LaShyLG8pLGE9ZVtzXTtlW3NdPWVbcl0sZVtyXT1hfXJldHVybiBlLmxlbmd0aD10LGV9dmFyIFBvLElvLEhvPShQbz1QcygoZnVuY3Rpb24oZSl7dmFyIHQ9W107cmV0dXJuIDQ2PT09ZS5jaGFyQ29kZUF0KDApJiZ0LnB1c2goIiIpLGUucmVwbGFjZShlZSwoZnVuY3Rpb24oZSxyLGksbil7dC5wdXNoKGk/bi5yZXBsYWNlKHVlLCIkMSIpOnJ8fGUpfSkpLHR9KSwoZnVuY3Rpb24oZSl7cmV0dXJuIDUwMD09PUlvLnNpemUmJklvLmNsZWFyKCksZX0pKSxJbz1Qby5jYWNoZSxQbyk7ZnVuY3Rpb24gam8oZSl7aWYoInN0cmluZyI9PXR5cGVvZiBlfHxsYShlKSlyZXR1cm4gZTt2YXIgdD1lKyIiO3JldHVybiIwIj09dCYmMS9lPT0tMS8wPyItMCI6dH1mdW5jdGlvbiBGbyhlKXtpZihudWxsIT1lKXt0cnl7cmV0dXJuIE9lLmNhbGwoZSl9Y2F0Y2goZSl7fXRyeXtyZXR1cm4gZSsiIn1jYXRjaChlKXt9fXJldHVybiIifWZ1bmN0aW9uIFdvKGUpe2lmKGUgaW5zdGFuY2VvZiBxcilyZXR1cm4gZS5jbG9uZSgpO3ZhciB0PW5ldyBVcihlLl9fd3JhcHBlZF9fLGUuX19jaGFpbl9fKTtyZXR1cm4gdC5fX2FjdGlvbnNfXz1BbihlLl9fYWN0aW9uc19fKSx0Ll9faW5kZXhfXz1lLl9faW5kZXhfXyx0Ll9fdmFsdWVzX189ZS5fX3ZhbHVlc19fLHR9dmFyIFVvPUdpKChmdW5jdGlvbihlLHQpe3JldHVybiBZcyhlKT9saShlLHBpKHQsMSxZcywhMCkpOltdfSkpLHFvPUdpKChmdW5jdGlvbihlLHQpe3ZhciByPUpvKHQpO3JldHVybiBZcyhyKSYmKHI9biksWXMoZSk/bGkoZSxwaSh0LDEsWXMsITApLHNvKHIsMikpOltdfSkpLE5vPUdpKChmdW5jdGlvbihlLHQpe3ZhciByPUpvKHQpO3JldHVybiBZcyhyKSYmKHI9biksWXMoZSk/bGkoZSxwaSh0LDEsWXMsITApLG4scik6W119KSk7ZnVuY3Rpb24gem8oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtpZighaSlyZXR1cm4tMTt2YXIgbj1udWxsPT1yPzA6cGEocik7cmV0dXJuIG48MCYmKG49dnIoaStuLDApKSxPdChlLHNvKHQsMyksbil9ZnVuY3Rpb24gS28oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtpZighaSlyZXR1cm4tMTt2YXIgbz1pLTE7cmV0dXJuIHIhPT1uJiYobz1wYShyKSxvPXI8MD92cihpK28sMCk6Z3IobyxpLTEpKSxPdChlLHNvKHQsMyksbywhMCl9ZnVuY3Rpb24gVm8oZSl7cmV0dXJuIG51bGwhPWUmJmUubGVuZ3RoP3BpKGUsMSk6W119ZnVuY3Rpb24gR28oZSl7cmV0dXJuIGUmJmUubGVuZ3RoP2VbMF06bn12YXIgWW89R2koKGZ1bmN0aW9uKGUpe3ZhciB0PUV0KGUscG4pO3JldHVybiB0Lmxlbmd0aCYmdFswXT09PWVbMF0/QWkodCk6W119KSksWG89R2koKGZ1bmN0aW9uKGUpe3ZhciB0PUpvKGUpLHI9RXQoZSxwbik7cmV0dXJuIHQ9PT1KbyhyKT90PW46ci5wb3AoKSxyLmxlbmd0aCYmclswXT09PWVbMF0/QWkocixzbyh0LDIpKTpbXX0pKSxabz1HaSgoZnVuY3Rpb24oZSl7dmFyIHQ9Sm8oZSkscj1FdChlLHBuKTtyZXR1cm4odD0iZnVuY3Rpb24iPT10eXBlb2YgdD90Om4pJiZyLnBvcCgpLHIubGVuZ3RoJiZyWzBdPT09ZVswXT9BaShyLG4sdCk6W119KSk7ZnVuY3Rpb24gSm8oZSl7dmFyIHQ9bnVsbD09ZT8wOmUubGVuZ3RoO3JldHVybiB0P2VbdC0xXTpufXZhciAkbz1HaShRbyk7ZnVuY3Rpb24gUW8oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGgmJnQmJnQubGVuZ3RoP05pKGUsdCk6ZX12YXIgZXM9ZW8oKGZ1bmN0aW9uKGUsdCl7dmFyIHI9bnVsbD09ZT8wOmUubGVuZ3RoLGk9bmkoZSx0KTtyZXR1cm4gemkoZSxFdCh0LChmdW5jdGlvbihlKXtyZXR1cm4gZ28oZSxyKT8rZTplfSkpLnNvcnQoTG4pKSxpfSkpO2Z1bmN0aW9uIHRzKGUpe3JldHVybiBudWxsPT1lP2U6U3IuY2FsbChlKX12YXIgcnM9R2koKGZ1bmN0aW9uKGUpe3JldHVybiBjbihwaShlLDEsWXMsITApKX0pKSxpcz1HaSgoZnVuY3Rpb24oZSl7dmFyIHQ9Sm8oZSk7cmV0dXJuIFlzKHQpJiYodD1uKSxjbihwaShlLDEsWXMsITApLHNvKHQsMikpfSkpLG5zPUdpKChmdW5jdGlvbihlKXt2YXIgdD1KbyhlKTtyZXR1cm4gdD0iZnVuY3Rpb24iPT10eXBlb2YgdD90Om4sY24ocGkoZSwxLFlzLCEwKSxuLHQpfSkpO2Z1bmN0aW9uIG9zKGUpe2lmKCFlfHwhZS5sZW5ndGgpcmV0dXJuW107dmFyIHQ9MDtyZXR1cm4gZT1DdChlLChmdW5jdGlvbihlKXtpZihZcyhlKSlyZXR1cm4gdD12cihlLmxlbmd0aCx0KSwhMH0pKSxVdCh0LChmdW5jdGlvbih0KXtyZXR1cm4gRXQoZSxIdCh0KSl9KSl9ZnVuY3Rpb24gc3MoZSx0KXtpZighZXx8IWUubGVuZ3RoKXJldHVybltdO3ZhciByPW9zKGUpO3JldHVybiBudWxsPT10P3I6RXQociwoZnVuY3Rpb24oZSl7cmV0dXJuIGd0KHQsbixlKX0pKX12YXIgYXM9R2koKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIFlzKGUpP2xpKGUsdCk6W119KSksY3M9R2koKGZ1bmN0aW9uKGUpe3JldHVybiBfbihDdChlLFlzKSl9KSksbHM9R2koKGZ1bmN0aW9uKGUpe3ZhciB0PUpvKGUpO3JldHVybiBZcyh0KSYmKHQ9biksX24oQ3QoZSxZcyksc28odCwyKSl9KSksdXM9R2koKGZ1bmN0aW9uKGUpe3ZhciB0PUpvKGUpO3JldHVybiB0PSJmdW5jdGlvbiI9PXR5cGVvZiB0P3Q6bixfbihDdChlLFlzKSxuLHQpfSkpLGhzPUdpKG9zKSxmcz1HaSgoZnVuY3Rpb24oZSl7dmFyIHQ9ZS5sZW5ndGgscj10PjE/ZVt0LTFdOm47cmV0dXJuIHI9ImZ1bmN0aW9uIj09dHlwZW9mIHI/KGUucG9wKCkscik6bixzcyhlLHIpfSkpO2Z1bmN0aW9uIF9zKGUpe3ZhciB0PWpyKGUpO3JldHVybiB0Ll9fY2hhaW5fXz0hMCx0fWZ1bmN0aW9uIGRzKGUsdCl7cmV0dXJuIHQoZSl9dmFyIHBzPWVvKChmdW5jdGlvbihlKXt2YXIgdD1lLmxlbmd0aCxyPXQ/ZVswXTowLGk9dGhpcy5fX3dyYXBwZWRfXyxvPWZ1bmN0aW9uKHQpe3JldHVybiBuaSh0LGUpfTtyZXR1cm4hKHQ+MXx8dGhpcy5fX2FjdGlvbnNfXy5sZW5ndGgpJiZpIGluc3RhbmNlb2YgcXImJmdvKHIpPygoaT1pLnNsaWNlKHIsK3IrKHQ/MTowKSkpLl9fYWN0aW9uc19fLnB1c2goe2Z1bmM6ZHMsYXJnczpbb10sdGhpc0FyZzpufSksbmV3IFVyKGksdGhpcy5fX2NoYWluX18pLnRocnUoKGZ1bmN0aW9uKGUpe3JldHVybiB0JiYhZS5sZW5ndGgmJmUucHVzaChuKSxlfSkpKTp0aGlzLnRocnUobyl9KSksdnM9TW4oKGZ1bmN0aW9uKGUsdCxyKXtCZS5jYWxsKGUscik/KytlW3JdOmlpKGUsciwxKX0pKSxncz1Jbih6bykseXM9SW4oS28pO2Z1bmN0aW9uIG1zKGUsdCl7cmV0dXJuKEtzKGUpP210OnVpKShlLHNvKHQsMykpfWZ1bmN0aW9uIGJzKGUsdCl7cmV0dXJuKEtzKGUpP2J0OmhpKShlLHNvKHQsMykpfXZhciBTcz1NbigoZnVuY3Rpb24oZSx0LHIpe0JlLmNhbGwoZSxyKT9lW3JdLnB1c2godCk6aWkoZSxyLFt0XSl9KSksQ3M9R2koKGZ1bmN0aW9uKGUsdCxyKXt2YXIgbj0tMSxvPSJmdW5jdGlvbiI9PXR5cGVvZiB0LHM9R3MoZSk/aShlLmxlbmd0aCk6W107cmV0dXJuIHVpKGUsKGZ1bmN0aW9uKGUpe3NbKytuXT1vP2d0KHQsZSxyKTpraShlLHQscil9KSksc30pKSx3cz1NbigoZnVuY3Rpb24oZSx0LHIpe2lpKGUscix0KX0pKTtmdW5jdGlvbiBMcyhlLHQpe3JldHVybihLcyhlKT9FdDpJaSkoZSxzbyh0LDMpKX12YXIgRXM9TW4oKGZ1bmN0aW9uKGUsdCxyKXtlW3I/MDoxXS5wdXNoKHQpfSksKGZ1bmN0aW9uKCl7cmV0dXJuW1tdLFtdXX0pKSx4cz1HaSgoZnVuY3Rpb24oZSx0KXtpZihudWxsPT1lKXJldHVybltdO3ZhciByPXQubGVuZ3RoO3JldHVybiByPjEmJnlvKGUsdFswXSx0WzFdKT90PVtdOnI+MiYmeW8odFswXSx0WzFdLHRbMl0pJiYodD1bdFswXV0pLFVpKGUscGkodCwxKSxbXSl9KSksQXM9UnR8fGZ1bmN0aW9uKCl7cmV0dXJuIG90LkRhdGUubm93KCl9O2Z1bmN0aW9uIGtzKGUsdCxyKXtyZXR1cm4gdD1yP246dCx0PWUmJm51bGw9PXQ/ZS5sZW5ndGg6dCxYbihlLGwsbixuLG4sbix0KX1mdW5jdGlvbiBNcyhlLHQpe3ZhciByO2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0KXRocm93IG5ldyBBZShvKTtyZXR1cm4gZT1wYShlKSxmdW5jdGlvbigpe3JldHVybi0tZT4wJiYocj10LmFwcGx5KHRoaXMsYXJndW1lbnRzKSksZTw9MSYmKHQ9bikscn19dmFyIFJzPUdpKChmdW5jdGlvbihlLHQscil7dmFyIGk9MTtpZihyLmxlbmd0aCl7dmFyIG49dHIocixvbyhScykpO2l8PWN9cmV0dXJuIFhuKGUsaSx0LHIsbil9KSksVHM9R2koKGZ1bmN0aW9uKGUsdCxyKXt2YXIgaT0zO2lmKHIubGVuZ3RoKXt2YXIgbj10cihyLG9vKFRzKSk7aXw9Y31yZXR1cm4gWG4odCxpLGUscixuKX0pKTtmdW5jdGlvbiBPcyhlLHQscil7dmFyIGkscyxhLGMsbCx1LGg9MCxmPSExLF89ITEsZD0hMDtpZigiZnVuY3Rpb24iIT10eXBlb2YgZSl0aHJvdyBuZXcgQWUobyk7ZnVuY3Rpb24gcCh0KXt2YXIgcj1pLG89cztyZXR1cm4gaT1zPW4saD10LGM9ZS5hcHBseShvLHIpfWZ1bmN0aW9uIHYoZSl7cmV0dXJuIGg9ZSxsPVJvKHksdCksZj9wKGUpOmN9ZnVuY3Rpb24gZyhlKXt2YXIgcj1lLXU7cmV0dXJuIHU9PT1ufHxyPj10fHxyPDB8fF8mJmUtaD49YX1mdW5jdGlvbiB5KCl7dmFyIGU9QXMoKTtpZihnKGUpKXJldHVybiBtKGUpO2w9Um8oeSxmdW5jdGlvbihlKXt2YXIgcj10LShlLXUpO3JldHVybiBfP2dyKHIsYS0oZS1oKSk6cn0oZSkpfWZ1bmN0aW9uIG0oZSl7cmV0dXJuIGw9bixkJiZpP3AoZSk6KGk9cz1uLGMpfWZ1bmN0aW9uIGIoKXt2YXIgZT1BcygpLHI9ZyhlKTtpZihpPWFyZ3VtZW50cyxzPXRoaXMsdT1lLHIpe2lmKGw9PT1uKXJldHVybiB2KHUpO2lmKF8pcmV0dXJuIGJuKGwpLGw9Um8oeSx0KSxwKHUpfXJldHVybiBsPT09biYmKGw9Um8oeSx0KSksY31yZXR1cm4gdD1nYSh0KXx8MCx0YShyKSYmKGY9ISFyLmxlYWRpbmcsYT0oXz0ibWF4V2FpdCJpbiByKT92cihnYShyLm1heFdhaXQpfHwwLHQpOmEsZD0idHJhaWxpbmciaW4gcj8hIXIudHJhaWxpbmc6ZCksYi5jYW5jZWw9ZnVuY3Rpb24oKXtsIT09biYmYm4obCksaD0wLGk9dT1zPWw9bn0sYi5mbHVzaD1mdW5jdGlvbigpe3JldHVybiBsPT09bj9jOm0oQXMoKSl9LGJ9dmFyIEJzPUdpKChmdW5jdGlvbihlLHQpe3JldHVybiBjaShlLDEsdCl9KSksRHM9R2koKGZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gY2koZSxnYSh0KXx8MCxyKX0pKTtmdW5jdGlvbiBQcyhlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiBlfHxudWxsIT10JiYiZnVuY3Rpb24iIT10eXBlb2YgdCl0aHJvdyBuZXcgQWUobyk7dmFyIHI9ZnVuY3Rpb24oKXt2YXIgaT1hcmd1bWVudHMsbj10P3QuYXBwbHkodGhpcyxpKTppWzBdLG89ci5jYWNoZTtpZihvLmhhcyhuKSlyZXR1cm4gby5nZXQobik7dmFyIHM9ZS5hcHBseSh0aGlzLGkpO3JldHVybiByLmNhY2hlPW8uc2V0KG4scyl8fG8sc307cmV0dXJuIHIuY2FjaGU9bmV3KFBzLkNhY2hlfHxLcikscn1mdW5jdGlvbiBJcyhlKXtpZigiZnVuY3Rpb24iIT10eXBlb2YgZSl0aHJvdyBuZXcgQWUobyk7cmV0dXJuIGZ1bmN0aW9uKCl7dmFyIHQ9YXJndW1lbnRzO3N3aXRjaCh0Lmxlbmd0aCl7Y2FzZSAwOnJldHVybiFlLmNhbGwodGhpcyk7Y2FzZSAxOnJldHVybiFlLmNhbGwodGhpcyx0WzBdKTtjYXNlIDI6cmV0dXJuIWUuY2FsbCh0aGlzLHRbMF0sdFsxXSk7Y2FzZSAzOnJldHVybiFlLmNhbGwodGhpcyx0WzBdLHRbMV0sdFsyXSl9cmV0dXJuIWUuYXBwbHkodGhpcyx0KX19UHMuQ2FjaGU9S3I7dmFyIEhzPXluKChmdW5jdGlvbihlLHQpe3ZhciByPSh0PTE9PXQubGVuZ3RoJiZLcyh0WzBdKT9FdCh0WzBdLE50KHNvKCkpKTpFdChwaSh0LDEpLE50KHNvKCkpKSkubGVuZ3RoO3JldHVybiBHaSgoZnVuY3Rpb24oaSl7Zm9yKHZhciBuPS0xLG89Z3IoaS5sZW5ndGgscik7KytuPG87KWlbbl09dFtuXS5jYWxsKHRoaXMsaVtuXSk7cmV0dXJuIGd0KGUsdGhpcyxpKX0pKX0pKSxqcz1HaSgoZnVuY3Rpb24oZSx0KXt2YXIgcj10cih0LG9vKGpzKSk7cmV0dXJuIFhuKGUsYyxuLHQscil9KSksRnM9R2koKGZ1bmN0aW9uKGUsdCl7dmFyIHI9dHIodCxvbyhGcykpO3JldHVybiBYbihlLDY0LG4sdCxyKX0pKSxXcz1lbygoZnVuY3Rpb24oZSx0KXtyZXR1cm4gWG4oZSwyNTYsbixuLG4sdCl9KSk7ZnVuY3Rpb24gVXMoZSx0KXtyZXR1cm4gZT09PXR8fGUhPWUmJnQhPXR9dmFyIHFzPXpuKExpKSxOcz16bigoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZT49dH0pKSx6cz1NaShmdW5jdGlvbigpe3JldHVybiBhcmd1bWVudHN9KCkpP01pOmZ1bmN0aW9uKGUpe3JldHVybiByYShlKSYmQmUuY2FsbChlLCJjYWxsZWUiKSYmIWV0LmNhbGwoZSwiY2FsbGVlIil9LEtzPWkuaXNBcnJheSxWcz1odD9OdChodCk6ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZ3aShlKT09VH07ZnVuY3Rpb24gR3MoZSl7cmV0dXJuIG51bGwhPWUmJmVhKGUubGVuZ3RoKSYmISRzKGUpfWZ1bmN0aW9uIFlzKGUpe3JldHVybiByYShlKSYmR3MoZSl9dmFyIFhzPWZyfHxnYyxacz1mdD9OdChmdCk6ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZ3aShlKT09eX07ZnVuY3Rpb24gSnMoZSl7aWYoIXJhKGUpKXJldHVybiExO3ZhciB0PXdpKGUpO3JldHVybiB0PT1tfHwiW29iamVjdCBET01FeGNlcHRpb25dIj09dHx8InN0cmluZyI9PXR5cGVvZiBlLm1lc3NhZ2UmJiJzdHJpbmciPT10eXBlb2YgZS5uYW1lJiYhb2EoZSl9ZnVuY3Rpb24gJHMoZSl7aWYoIXRhKGUpKXJldHVybiExO3ZhciB0PXdpKGUpO3JldHVybiB0PT1ifHx0PT1TfHwiW29iamVjdCBBc3luY0Z1bmN0aW9uXSI9PXR8fCJbb2JqZWN0IFByb3h5XSI9PXR9ZnVuY3Rpb24gUXMoZSl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlJiZlPT1wYShlKX1mdW5jdGlvbiBlYShlKXtyZXR1cm4ibnVtYmVyIj09dHlwZW9mIGUmJmU+LTEmJmUlMT09MCYmZTw9aH1mdW5jdGlvbiB0YShlKXt2YXIgdD10eXBlb2YgZTtyZXR1cm4gbnVsbCE9ZSYmKCJvYmplY3QiPT10fHwiZnVuY3Rpb24iPT10KX1mdW5jdGlvbiByYShlKXtyZXR1cm4gbnVsbCE9ZSYmIm9iamVjdCI9PXR5cGVvZiBlfXZhciBpYT1fdD9OdChfdCk6ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZmbyhlKT09Q307ZnVuY3Rpb24gbmEoZSl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlfHxyYShlKSYmd2koZSk9PXd9ZnVuY3Rpb24gb2EoZSl7aWYoIXJhKGUpfHx3aShlKSE9TClyZXR1cm4hMTt2YXIgdD1WZShlKTtpZihudWxsPT09dClyZXR1cm4hMDt2YXIgcj1CZS5jYWxsKHQsImNvbnN0cnVjdG9yIikmJnQuY29uc3RydWN0b3I7cmV0dXJuImZ1bmN0aW9uIj09dHlwZW9mIHImJnIgaW5zdGFuY2VvZiByJiZPZS5jYWxsKHIpPT1IZX12YXIgc2E9ZHQ/TnQoZHQpOmZ1bmN0aW9uKGUpe3JldHVybiByYShlKSYmd2koZSk9PXh9LGFhPXB0P050KHB0KTpmdW5jdGlvbihlKXtyZXR1cm4gcmEoZSkmJmZvKGUpPT1BfTtmdW5jdGlvbiBjYShlKXtyZXR1cm4ic3RyaW5nIj09dHlwZW9mIGV8fCFLcyhlKSYmcmEoZSkmJndpKGUpPT1rfWZ1bmN0aW9uIGxhKGUpe3JldHVybiJzeW1ib2wiPT10eXBlb2YgZXx8cmEoZSkmJndpKGUpPT1NfXZhciB1YT12dD9OdCh2dCk6ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZlYShlLmxlbmd0aCkmJiEhJGVbd2koZSldfSxoYT16bihQaSksZmE9em4oKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGU8PXR9KSk7ZnVuY3Rpb24gX2EoZSl7aWYoIWUpcmV0dXJuW107aWYoR3MoZSkpcmV0dXJuIGNhKGUpP29yKGUpOkFuKGUpO2lmKHN0JiZlW3N0XSlyZXR1cm4gZnVuY3Rpb24oZSl7Zm9yKHZhciB0LHI9W107ISh0PWUubmV4dCgpKS5kb25lOylyLnB1c2godC52YWx1ZSk7cmV0dXJuIHJ9KGVbc3RdKCkpO3ZhciB0PWZvKGUpO3JldHVybih0PT1DP1F0OnQ9PUE/cnI6VWEpKGUpfWZ1bmN0aW9uIGRhKGUpe3JldHVybiBlPyhlPWdhKGUpKT09PXV8fGU9PT0tMS8wPzE3OTc2OTMxMzQ4NjIzMTU3ZTI5MiooZTwwPy0xOjEpOmU9PWU/ZTowOjA9PT1lP2U6MH1mdW5jdGlvbiBwYShlKXt2YXIgdD1kYShlKSxyPXQlMTtyZXR1cm4gdD09dD9yP3Qtcjp0OjB9ZnVuY3Rpb24gdmEoZSl7cmV0dXJuIGU/b2kocGEoZSksMCxfKTowfWZ1bmN0aW9uIGdhKGUpe2lmKCJudW1iZXIiPT10eXBlb2YgZSlyZXR1cm4gZTtpZihsYShlKSlyZXR1cm4gZjtpZih0YShlKSl7dmFyIHQ9ImZ1bmN0aW9uIj09dHlwZW9mIGUudmFsdWVPZj9lLnZhbHVlT2YoKTplO2U9dGEodCk/dCsiIjp0fWlmKCJzdHJpbmciIT10eXBlb2YgZSlyZXR1cm4gMD09PWU/ZTorZTtlPXF0KGUpO3ZhciByPWRlLnRlc3QoZSk7cmV0dXJuIHJ8fHZlLnRlc3QoZSk/cnQoZS5zbGljZSgyKSxyPzI6OCk6X2UudGVzdChlKT9mOitlfWZ1bmN0aW9uIHlhKGUpe3JldHVybiBrbihlLEJhKGUpKX1mdW5jdGlvbiBtYShlKXtyZXR1cm4gbnVsbD09ZT8iIjphbihlKX12YXIgYmE9Um4oKGZ1bmN0aW9uKGUsdCl7aWYoQ28odCl8fEdzKHQpKWtuKHQsT2EodCksZSk7ZWxzZSBmb3IodmFyIHIgaW4gdClCZS5jYWxsKHQscikmJlFyKGUscix0W3JdKX0pKSxTYT1SbigoZnVuY3Rpb24oZSx0KXtrbih0LEJhKHQpLGUpfSkpLENhPVJuKChmdW5jdGlvbihlLHQscixpKXtrbih0LEJhKHQpLGUsaSl9KSksd2E9Um4oKGZ1bmN0aW9uKGUsdCxyLGkpe2tuKHQsT2EodCksZSxpKX0pKSxMYT1lbyhuaSksRWE9R2koKGZ1bmN0aW9uKGUsdCl7ZT1MZShlKTt2YXIgcj0tMSxpPXQubGVuZ3RoLG89aT4yP3RbMl06bjtmb3IobyYmeW8odFswXSx0WzFdLG8pJiYoaT0xKTsrK3I8aTspZm9yKHZhciBzPXRbcl0sYT1CYShzKSxjPS0xLGw9YS5sZW5ndGg7KytjPGw7KXt2YXIgdT1hW2NdLGg9ZVt1XTsoaD09PW58fFVzKGgsUmVbdV0pJiYhQmUuY2FsbChlLHUpKSYmKGVbdV09c1t1XSl9cmV0dXJuIGV9KSkseGE9R2koKGZ1bmN0aW9uKGUpe3JldHVybiBlLnB1c2gobixKbiksZ3QoUGEsbixlKX0pKTtmdW5jdGlvbiBBYShlLHQscil7dmFyIGk9bnVsbD09ZT9uOlNpKGUsdCk7cmV0dXJuIGk9PT1uP3I6aX1mdW5jdGlvbiBrYShlLHQpe3JldHVybiBudWxsIT1lJiZfbyhlLHQseGkpfXZhciBNYT1GbigoZnVuY3Rpb24oZSx0LHIpe251bGwhPXQmJiJmdW5jdGlvbiIhPXR5cGVvZiB0LnRvU3RyaW5nJiYodD1JZS5jYWxsKHQpKSxlW3RdPXJ9KSx0YyhuYykpLFJhPUZuKChmdW5jdGlvbihlLHQscil7bnVsbCE9dCYmImZ1bmN0aW9uIiE9dHlwZW9mIHQudG9TdHJpbmcmJih0PUllLmNhbGwodCkpLEJlLmNhbGwoZSx0KT9lW3RdLnB1c2gocik6ZVt0XT1bcl19KSxzbyksVGE9R2koa2kpO2Z1bmN0aW9uIE9hKGUpe3JldHVybiBHcyhlKT9ZcihlKTpEaShlKX1mdW5jdGlvbiBCYShlKXtyZXR1cm4gR3MoZSk/WXIoZSwhMCk6ZnVuY3Rpb24oZSl7aWYoIXRhKGUpKXJldHVybiBmdW5jdGlvbihlKXt2YXIgdD1bXTtpZihudWxsIT1lKWZvcih2YXIgciBpbiBMZShlKSl0LnB1c2gocik7cmV0dXJuIHR9KGUpO3ZhciB0PUNvKGUpLHI9W107Zm9yKHZhciBpIGluIGUpKCJjb25zdHJ1Y3RvciIhPWl8fCF0JiZCZS5jYWxsKGUsaSkpJiZyLnB1c2goaSk7cmV0dXJuIHJ9KGUpfXZhciBEYT1SbigoZnVuY3Rpb24oZSx0LHIpe0ZpKGUsdCxyKX0pKSxQYT1SbigoZnVuY3Rpb24oZSx0LHIsaSl7RmkoZSx0LHIsaSl9KSksSWE9ZW8oKGZ1bmN0aW9uKGUsdCl7dmFyIHI9e307aWYobnVsbD09ZSlyZXR1cm4gcjt2YXIgaT0hMTt0PUV0KHQsKGZ1bmN0aW9uKHQpe3JldHVybiB0PWduKHQsZSksaXx8KGk9dC5sZW5ndGg+MSksdH0pKSxrbihlLHJvKGUpLHIpLGkmJihyPXNpKHIsNywkbikpO2Zvcih2YXIgbj10Lmxlbmd0aDtuLS07KWxuKHIsdFtuXSk7cmV0dXJuIHJ9KSksSGE9ZW8oKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIG51bGw9PWU/e306ZnVuY3Rpb24oZSx0KXtyZXR1cm4gcWkoZSx0LChmdW5jdGlvbih0LHIpe3JldHVybiBrYShlLHIpfSkpfShlLHQpfSkpO2Z1bmN0aW9uIGphKGUsdCl7aWYobnVsbD09ZSlyZXR1cm57fTt2YXIgcj1FdChybyhlKSwoZnVuY3Rpb24oZSl7cmV0dXJuW2VdfSkpO3JldHVybiB0PXNvKHQpLHFpKGUsciwoZnVuY3Rpb24oZSxyKXtyZXR1cm4gdChlLHJbMF0pfSkpfXZhciBGYT1ZbihPYSksV2E9WW4oQmEpO2Z1bmN0aW9uIFVhKGUpe3JldHVybiBudWxsPT1lP1tdOnp0KGUsT2EoZSkpfXZhciBxYT1EbigoZnVuY3Rpb24oZSx0LHIpe3JldHVybiB0PXQudG9Mb3dlckNhc2UoKSxlKyhyP05hKHQpOnQpfSkpO2Z1bmN0aW9uIE5hKGUpe3JldHVybiBKYShtYShlKS50b0xvd2VyQ2FzZSgpKX1mdW5jdGlvbiB6YShlKXtyZXR1cm4oZT1tYShlKSkmJmUucmVwbGFjZSh5ZSxYdCkucmVwbGFjZShLZSwiIil9dmFyIEthPURuKChmdW5jdGlvbihlLHQscil7cmV0dXJuIGUrKHI/Ii0iOiIiKSt0LnRvTG93ZXJDYXNlKCl9KSksVmE9RG4oKGZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gZSsocj8iICI6IiIpK3QudG9Mb3dlckNhc2UoKX0pKSxHYT1CbigidG9Mb3dlckNhc2UiKSxZYT1EbigoZnVuY3Rpb24oZSx0LHIpe3JldHVybiBlKyhyPyJfIjoiIikrdC50b0xvd2VyQ2FzZSgpfSkpLFhhPURuKChmdW5jdGlvbihlLHQscil7cmV0dXJuIGUrKHI/IiAiOiIiKStKYSh0KX0pKSxaYT1EbigoZnVuY3Rpb24oZSx0LHIpe3JldHVybiBlKyhyPyIgIjoiIikrdC50b1VwcGVyQ2FzZSgpfSkpLEphPUJuKCJ0b1VwcGVyQ2FzZSIpO2Z1bmN0aW9uICRhKGUsdCxyKXtyZXR1cm4gZT1tYShlKSwodD1yP246dCk9PT1uP2Z1bmN0aW9uKGUpe3JldHVybiBYZS50ZXN0KGUpfShlKT9mdW5jdGlvbihlKXtyZXR1cm4gZS5tYXRjaChHZSl8fFtdfShlKTpmdW5jdGlvbihlKXtyZXR1cm4gZS5tYXRjaChjZSl8fFtdfShlKTplLm1hdGNoKHQpfHxbXX12YXIgUWE9R2koKGZ1bmN0aW9uKGUsdCl7dHJ5e3JldHVybiBndChlLG4sdCl9Y2F0Y2goZSl7cmV0dXJuIEpzKGUpP2U6bmV3IFNlKGUpfX0pKSxlYz1lbygoZnVuY3Rpb24oZSx0KXtyZXR1cm4gbXQodCwoZnVuY3Rpb24odCl7dD1qbyh0KSxpaShlLHQsUnMoZVt0XSxlKSl9KSksZX0pKTtmdW5jdGlvbiB0YyhlKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZX19dmFyIHJjPUhuKCksaWM9SG4oITApO2Z1bmN0aW9uIG5jKGUpe3JldHVybiBlfWZ1bmN0aW9uIG9jKGUpe3JldHVybiBCaSgiZnVuY3Rpb24iPT10eXBlb2YgZT9lOnNpKGUsMSkpfXZhciBzYz1HaSgoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocil7cmV0dXJuIGtpKHIsZSx0KX19KSksYWM9R2koKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIpe3JldHVybiBraShlLHIsdCl9fSkpO2Z1bmN0aW9uIGNjKGUsdCxyKXt2YXIgaT1PYSh0KSxuPWJpKHQsaSk7bnVsbCE9cnx8dGEodCkmJihuLmxlbmd0aHx8IWkubGVuZ3RoKXx8KHI9dCx0PWUsZT10aGlzLG49YmkodCxPYSh0KSkpO3ZhciBvPSEodGEocikmJiJjaGFpbiJpbiByJiYhci5jaGFpbikscz0kcyhlKTtyZXR1cm4gbXQobiwoZnVuY3Rpb24ocil7dmFyIGk9dFtyXTtlW3JdPWkscyYmKGUucHJvdG90eXBlW3JdPWZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy5fX2NoYWluX187aWYob3x8dCl7dmFyIHI9ZSh0aGlzLl9fd3JhcHBlZF9fKSxuPXIuX19hY3Rpb25zX189QW4odGhpcy5fX2FjdGlvbnNfXyk7cmV0dXJuIG4ucHVzaCh7ZnVuYzppLGFyZ3M6YXJndW1lbnRzLHRoaXNBcmc6ZX0pLHIuX19jaGFpbl9fPXQscn1yZXR1cm4gaS5hcHBseShlLHh0KFt0aGlzLnZhbHVlKCldLGFyZ3VtZW50cykpfSl9KSksZX1mdW5jdGlvbiBsYygpe312YXIgdWM9VW4oRXQpLGhjPVVuKFN0KSxmYz1VbihNdCk7ZnVuY3Rpb24gX2MoZSl7cmV0dXJuIG1vKGUpP0h0KGpvKGUpKTpmdW5jdGlvbihlKXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIFNpKHQsZSl9fShlKX12YXIgZGM9Tm4oKSxwYz1ObighMCk7ZnVuY3Rpb24gdmMoKXtyZXR1cm5bXX1mdW5jdGlvbiBnYygpe3JldHVybiExfXZhciB5YyxtYz1XbigoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSt0fSksMCksYmM9Vm4oImNlaWwiKSxTYz1XbigoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZS90fSksMSksQ2M9Vm4oImZsb29yIiksd2M9V24oKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUqdH0pLDEpLExjPVZuKCJyb3VuZCIpLEVjPVduKChmdW5jdGlvbihlLHQpe3JldHVybiBlLXR9KSwwKTtyZXR1cm4ganIuYWZ0ZXI9ZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgdCl0aHJvdyBuZXcgQWUobyk7cmV0dXJuIGU9cGEoZSksZnVuY3Rpb24oKXtpZigtLWU8MSlyZXR1cm4gdC5hcHBseSh0aGlzLGFyZ3VtZW50cyl9fSxqci5hcnk9a3MsanIuYXNzaWduPWJhLGpyLmFzc2lnbkluPVNhLGpyLmFzc2lnbkluV2l0aD1DYSxqci5hc3NpZ25XaXRoPXdhLGpyLmF0PUxhLGpyLmJlZm9yZT1Ncyxqci5iaW5kPVJzLGpyLmJpbmRBbGw9ZWMsanIuYmluZEtleT1Ucyxqci5jYXN0QXJyYXk9ZnVuY3Rpb24oKXtpZighYXJndW1lbnRzLmxlbmd0aClyZXR1cm5bXTt2YXIgZT1hcmd1bWVudHNbMF07cmV0dXJuIEtzKGUpP2U6W2VdfSxqci5jaGFpbj1fcyxqci5jaHVuaz1mdW5jdGlvbihlLHQscil7dD0ocj95byhlLHQscik6dD09PW4pPzE6dnIocGEodCksMCk7dmFyIG89bnVsbD09ZT8wOmUubGVuZ3RoO2lmKCFvfHx0PDEpcmV0dXJuW107Zm9yKHZhciBzPTAsYT0wLGM9aShscihvL3QpKTtzPG87KWNbYSsrXT1lbihlLHMscys9dCk7cmV0dXJuIGN9LGpyLmNvbXBhY3Q9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoLGk9MCxuPVtdOysrdDxyOyl7dmFyIG89ZVt0XTtvJiYobltpKytdPW8pfXJldHVybiBufSxqci5jb25jYXQ9ZnVuY3Rpb24oKXt2YXIgZT1hcmd1bWVudHMubGVuZ3RoO2lmKCFlKXJldHVybltdO2Zvcih2YXIgdD1pKGUtMSkscj1hcmd1bWVudHNbMF0sbj1lO24tLTspdFtuLTFdPWFyZ3VtZW50c1tuXTtyZXR1cm4geHQoS3Mocik/QW4ocik6W3JdLHBpKHQsMSkpfSxqci5jb25kPWZ1bmN0aW9uKGUpe3ZhciB0PW51bGw9PWU/MDplLmxlbmd0aCxyPXNvKCk7cmV0dXJuIGU9dD9FdChlLChmdW5jdGlvbihlKXtpZigiZnVuY3Rpb24iIT10eXBlb2YgZVsxXSl0aHJvdyBuZXcgQWUobyk7cmV0dXJuW3IoZVswXSksZVsxXV19KSk6W10sR2koKGZ1bmN0aW9uKHIpe2Zvcih2YXIgaT0tMTsrK2k8dDspe3ZhciBuPWVbaV07aWYoZ3QoblswXSx0aGlzLHIpKXJldHVybiBndChuWzFdLHRoaXMscil9fSkpfSxqci5jb25mb3Jtcz1mdW5jdGlvbihlKXtyZXR1cm4gZnVuY3Rpb24oZSl7dmFyIHQ9T2EoZSk7cmV0dXJuIGZ1bmN0aW9uKHIpe3JldHVybiBhaShyLGUsdCl9fShzaShlLDEpKX0sanIuY29uc3RhbnQ9dGMsanIuY291bnRCeT12cyxqci5jcmVhdGU9ZnVuY3Rpb24oZSx0KXt2YXIgcj1GcihlKTtyZXR1cm4gbnVsbD09dD9yOnJpKHIsdCl9LGpyLmN1cnJ5PWZ1bmN0aW9uIGUodCxyLGkpe3ZhciBvPVhuKHQsOCxuLG4sbixuLG4scj1pP246cik7cmV0dXJuIG8ucGxhY2Vob2xkZXI9ZS5wbGFjZWhvbGRlcixvfSxqci5jdXJyeVJpZ2h0PWZ1bmN0aW9uIGUodCxyLGkpe3ZhciBvPVhuKHQsMTYsbixuLG4sbixuLHI9aT9uOnIpO3JldHVybiBvLnBsYWNlaG9sZGVyPWUucGxhY2Vob2xkZXIsb30sanIuZGVib3VuY2U9T3MsanIuZGVmYXVsdHM9RWEsanIuZGVmYXVsdHNEZWVwPXhhLGpyLmRlZmVyPUJzLGpyLmRlbGF5PURzLGpyLmRpZmZlcmVuY2U9VW8sanIuZGlmZmVyZW5jZUJ5PXFvLGpyLmRpZmZlcmVuY2VXaXRoPU5vLGpyLmRyb3A9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtyZXR1cm4gaT9lbihlLCh0PXJ8fHQ9PT1uPzE6cGEodCkpPDA/MDp0LGkpOltdfSxqci5kcm9wUmlnaHQ9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtyZXR1cm4gaT9lbihlLDAsKHQ9aS0odD1yfHx0PT09bj8xOnBhKHQpKSk8MD8wOnQpOltdfSxqci5kcm9wUmlnaHRXaGlsZT1mdW5jdGlvbihlLHQpe3JldHVybiBlJiZlLmxlbmd0aD9obihlLHNvKHQsMyksITAsITApOltdfSxqci5kcm9wV2hpbGU9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/aG4oZSxzbyh0LDMpLCEwKTpbXX0sanIuZmlsbD1mdW5jdGlvbihlLHQscixpKXt2YXIgbz1udWxsPT1lPzA6ZS5sZW5ndGg7cmV0dXJuIG8/KHImJiJudW1iZXIiIT10eXBlb2YgciYmeW8oZSx0LHIpJiYocj0wLGk9byksZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG89ZS5sZW5ndGg7Zm9yKChyPXBhKHIpKTwwJiYocj0tcj5vPzA6bytyKSwoaT1pPT09bnx8aT5vP286cGEoaSkpPDAmJihpKz1vKSxpPXI+aT8wOnZhKGkpO3I8aTspZVtyKytdPXQ7cmV0dXJuIGV9KGUsdCxyLGkpKTpbXX0sanIuZmlsdGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuKEtzKGUpP0N0OmRpKShlLHNvKHQsMykpfSxqci5mbGF0TWFwPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHBpKExzKGUsdCksMSl9LGpyLmZsYXRNYXBEZWVwPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHBpKExzKGUsdCksdSl9LGpyLmZsYXRNYXBEZXB0aD1mdW5jdGlvbihlLHQscil7cmV0dXJuIHI9cj09PW4/MTpwYShyKSxwaShMcyhlLHQpLHIpfSxqci5mbGF0dGVuPVZvLGpyLmZsYXR0ZW5EZWVwPWZ1bmN0aW9uKGUpe3JldHVybiBudWxsIT1lJiZlLmxlbmd0aD9waShlLHUpOltdfSxqci5mbGF0dGVuRGVwdGg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gbnVsbCE9ZSYmZS5sZW5ndGg/cGkoZSx0PXQ9PT1uPzE6cGEodCkpOltdfSxqci5mbGlwPWZ1bmN0aW9uKGUpe3JldHVybiBYbihlLDUxMil9LGpyLmZsb3c9cmMsanIuZmxvd1JpZ2h0PWljLGpyLmZyb21QYWlycz1mdW5jdGlvbihlKXtmb3IodmFyIHQ9LTEscj1udWxsPT1lPzA6ZS5sZW5ndGgsaT17fTsrK3Q8cjspe3ZhciBuPWVbdF07aVtuWzBdXT1uWzFdfXJldHVybiBpfSxqci5mdW5jdGlvbnM9ZnVuY3Rpb24oZSl7cmV0dXJuIG51bGw9PWU/W106YmkoZSxPYShlKSl9LGpyLmZ1bmN0aW9uc0luPWZ1bmN0aW9uKGUpe3JldHVybiBudWxsPT1lP1tdOmJpKGUsQmEoZSkpfSxqci5ncm91cEJ5PVNzLGpyLmluaXRpYWw9ZnVuY3Rpb24oZSl7cmV0dXJuIG51bGwhPWUmJmUubGVuZ3RoP2VuKGUsMCwtMSk6W119LGpyLmludGVyc2VjdGlvbj1Zbyxqci5pbnRlcnNlY3Rpb25CeT1Ybyxqci5pbnRlcnNlY3Rpb25XaXRoPVpvLGpyLmludmVydD1NYSxqci5pbnZlcnRCeT1SYSxqci5pbnZva2VNYXA9Q3MsanIuaXRlcmF0ZWU9b2MsanIua2V5Qnk9d3MsanIua2V5cz1PYSxqci5rZXlzSW49QmEsanIubWFwPUxzLGpyLm1hcEtleXM9ZnVuY3Rpb24oZSx0KXt2YXIgcj17fTtyZXR1cm4gdD1zbyh0LDMpLHlpKGUsKGZ1bmN0aW9uKGUsaSxuKXtpaShyLHQoZSxpLG4pLGUpfSkpLHJ9LGpyLm1hcFZhbHVlcz1mdW5jdGlvbihlLHQpe3ZhciByPXt9O3JldHVybiB0PXNvKHQsMykseWkoZSwoZnVuY3Rpb24oZSxpLG4pe2lpKHIsaSx0KGUsaSxuKSl9KSkscn0sanIubWF0Y2hlcz1mdW5jdGlvbihlKXtyZXR1cm4gSGkoc2koZSwxKSl9LGpyLm1hdGNoZXNQcm9wZXJ0eT1mdW5jdGlvbihlLHQpe3JldHVybiBqaShlLHNpKHQsMSkpfSxqci5tZW1vaXplPVBzLGpyLm1lcmdlPURhLGpyLm1lcmdlV2l0aD1QYSxqci5tZXRob2Q9c2MsanIubWV0aG9kT2Y9YWMsanIubWl4aW49Y2MsanIubmVnYXRlPUlzLGpyLm50aEFyZz1mdW5jdGlvbihlKXtyZXR1cm4gZT1wYShlKSxHaSgoZnVuY3Rpb24odCl7cmV0dXJuIFdpKHQsZSl9KSl9LGpyLm9taXQ9SWEsanIub21pdEJ5PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGphKGUsSXMoc28odCkpKX0sanIub25jZT1mdW5jdGlvbihlKXtyZXR1cm4gTXMoMixlKX0sanIub3JkZXJCeT1mdW5jdGlvbihlLHQscixpKXtyZXR1cm4gbnVsbD09ZT9bXTooS3ModCl8fCh0PW51bGw9PXQ/W106W3RdKSxLcyhyPWk/bjpyKXx8KHI9bnVsbD09cj9bXTpbcl0pLFVpKGUsdCxyKSl9LGpyLm92ZXI9dWMsanIub3ZlckFyZ3M9SHMsanIub3ZlckV2ZXJ5PWhjLGpyLm92ZXJTb21lPWZjLGpyLnBhcnRpYWw9anMsanIucGFydGlhbFJpZ2h0PUZzLGpyLnBhcnRpdGlvbj1Fcyxqci5waWNrPUhhLGpyLnBpY2tCeT1qYSxqci5wcm9wZXJ0eT1fYyxqci5wcm9wZXJ0eU9mPWZ1bmN0aW9uKGUpe3JldHVybiBmdW5jdGlvbih0KXtyZXR1cm4gbnVsbD09ZT9uOlNpKGUsdCl9fSxqci5wdWxsPSRvLGpyLnB1bGxBbGw9UW8sanIucHVsbEFsbEJ5PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gZSYmZS5sZW5ndGgmJnQmJnQubGVuZ3RoP05pKGUsdCxzbyhyLDIpKTplfSxqci5wdWxsQWxsV2l0aD1mdW5jdGlvbihlLHQscil7cmV0dXJuIGUmJmUubGVuZ3RoJiZ0JiZ0Lmxlbmd0aD9OaShlLHQsbixyKTplfSxqci5wdWxsQXQ9ZXMsanIucmFuZ2U9ZGMsanIucmFuZ2VSaWdodD1wYyxqci5yZWFyZz1Xcyxqci5yZWplY3Q9ZnVuY3Rpb24oZSx0KXtyZXR1cm4oS3MoZSk/Q3Q6ZGkpKGUsSXMoc28odCwzKSkpfSxqci5yZW1vdmU9ZnVuY3Rpb24oZSx0KXt2YXIgcj1bXTtpZighZXx8IWUubGVuZ3RoKXJldHVybiByO3ZhciBpPS0xLG49W10sbz1lLmxlbmd0aDtmb3IodD1zbyh0LDMpOysraTxvOyl7dmFyIHM9ZVtpXTt0KHMsaSxlKSYmKHIucHVzaChzKSxuLnB1c2goaSkpfXJldHVybiB6aShlLG4pLHJ9LGpyLnJlc3Q9ZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgZSl0aHJvdyBuZXcgQWUobyk7cmV0dXJuIEdpKGUsdD10PT09bj90OnBhKHQpKX0sanIucmV2ZXJzZT10cyxqci5zYW1wbGVTaXplPWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gdD0ocj95byhlLHQscik6dD09PW4pPzE6cGEodCksKEtzKGUpP1pyOlhpKShlLHQpfSxqci5zZXQ9ZnVuY3Rpb24oZSx0LHIpe3JldHVybiBudWxsPT1lP2U6WmkoZSx0LHIpfSxqci5zZXRXaXRoPWZ1bmN0aW9uKGUsdCxyLGkpe3JldHVybiBpPSJmdW5jdGlvbiI9PXR5cGVvZiBpP2k6bixudWxsPT1lP2U6WmkoZSx0LHIsaSl9LGpyLnNodWZmbGU9ZnVuY3Rpb24oZSl7cmV0dXJuKEtzKGUpP0pyOlFpKShlKX0sanIuc2xpY2U9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtyZXR1cm4gaT8ociYmIm51bWJlciIhPXR5cGVvZiByJiZ5byhlLHQscik/KHQ9MCxyPWkpOih0PW51bGw9PXQ/MDpwYSh0KSxyPXI9PT1uP2k6cGEocikpLGVuKGUsdCxyKSk6W119LGpyLnNvcnRCeT14cyxqci5zb3J0ZWRVbmlxPWZ1bmN0aW9uKGUpe3JldHVybiBlJiZlLmxlbmd0aD9vbihlKTpbXX0sanIuc29ydGVkVW5pcUJ5PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUmJmUubGVuZ3RoP29uKGUsc28odCwyKSk6W119LGpyLnNwbGl0PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gciYmIm51bWJlciIhPXR5cGVvZiByJiZ5byhlLHQscikmJih0PXI9biksKHI9cj09PW4/XzpyPj4+MCk/KGU9bWEoZSkpJiYoInN0cmluZyI9PXR5cGVvZiB0fHxudWxsIT10JiYhc2EodCkpJiYhKHQ9YW4odCkpJiYkdChlKT9tbihvcihlKSwwLHIpOmUuc3BsaXQodCxyKTpbXX0sanIuc3ByZWFkPWZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIGUpdGhyb3cgbmV3IEFlKG8pO3JldHVybiB0PW51bGw9PXQ/MDp2cihwYSh0KSwwKSxHaSgoZnVuY3Rpb24ocil7dmFyIGk9clt0XSxuPW1uKHIsMCx0KTtyZXR1cm4gaSYmeHQobixpKSxndChlLHRoaXMsbil9KSl9LGpyLnRhaWw9ZnVuY3Rpb24oZSl7dmFyIHQ9bnVsbD09ZT8wOmUubGVuZ3RoO3JldHVybiB0P2VuKGUsMSx0KTpbXX0sanIudGFrZT1mdW5jdGlvbihlLHQscil7cmV0dXJuIGUmJmUubGVuZ3RoP2VuKGUsMCwodD1yfHx0PT09bj8xOnBhKHQpKTwwPzA6dCk6W119LGpyLnRha2VSaWdodD1mdW5jdGlvbihlLHQscil7dmFyIGk9bnVsbD09ZT8wOmUubGVuZ3RoO3JldHVybiBpP2VuKGUsKHQ9aS0odD1yfHx0PT09bj8xOnBhKHQpKSk8MD8wOnQsaSk6W119LGpyLnRha2VSaWdodFdoaWxlPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUmJmUubGVuZ3RoP2huKGUsc28odCwzKSwhMSwhMCk6W119LGpyLnRha2VXaGlsZT1mdW5jdGlvbihlLHQpe3JldHVybiBlJiZlLmxlbmd0aD9obihlLHNvKHQsMykpOltdfSxqci50YXA9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdChlKSxlfSxqci50aHJvdHRsZT1mdW5jdGlvbihlLHQscil7dmFyIGk9ITAsbj0hMDtpZigiZnVuY3Rpb24iIT10eXBlb2YgZSl0aHJvdyBuZXcgQWUobyk7cmV0dXJuIHRhKHIpJiYoaT0ibGVhZGluZyJpbiByPyEhci5sZWFkaW5nOmksbj0idHJhaWxpbmciaW4gcj8hIXIudHJhaWxpbmc6biksT3MoZSx0LHtsZWFkaW5nOmksbWF4V2FpdDp0LHRyYWlsaW5nOm59KX0sanIudGhydT1kcyxqci50b0FycmF5PV9hLGpyLnRvUGFpcnM9RmEsanIudG9QYWlyc0luPVdhLGpyLnRvUGF0aD1mdW5jdGlvbihlKXtyZXR1cm4gS3MoZSk/RXQoZSxqbyk6bGEoZSk/W2VdOkFuKEhvKG1hKGUpKSl9LGpyLnRvUGxhaW5PYmplY3Q9eWEsanIudHJhbnNmb3JtPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1LcyhlKSxuPWl8fFhzKGUpfHx1YShlKTtpZih0PXNvKHQsNCksbnVsbD09cil7dmFyIG89ZSYmZS5jb25zdHJ1Y3RvcjtyPW4/aT9uZXcgbzpbXTp0YShlKSYmJHMobyk/RnIoVmUoZSkpOnt9fXJldHVybihuP210OnlpKShlLChmdW5jdGlvbihlLGksbil7cmV0dXJuIHQocixlLGksbil9KSkscn0sanIudW5hcnk9ZnVuY3Rpb24oZSl7cmV0dXJuIGtzKGUsMSl9LGpyLnVuaW9uPXJzLGpyLnVuaW9uQnk9aXMsanIudW5pb25XaXRoPW5zLGpyLnVuaXE9ZnVuY3Rpb24oZSl7cmV0dXJuIGUmJmUubGVuZ3RoP2NuKGUpOltdfSxqci51bmlxQnk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/Y24oZSxzbyh0LDIpKTpbXX0sanIudW5pcVdpdGg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdD0iZnVuY3Rpb24iPT10eXBlb2YgdD90Om4sZSYmZS5sZW5ndGg/Y24oZSxuLHQpOltdfSxqci51bnNldD1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT1lfHxsbihlLHQpfSxqci51bnppcD1vcyxqci51bnppcFdpdGg9c3MsanIudXBkYXRlPWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gbnVsbD09ZT9lOnVuKGUsdCx2bihyKSl9LGpyLnVwZGF0ZVdpdGg9ZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuIGk9ImZ1bmN0aW9uIj09dHlwZW9mIGk/aTpuLG51bGw9PWU/ZTp1bihlLHQsdm4ociksaSl9LGpyLnZhbHVlcz1VYSxqci52YWx1ZXNJbj1mdW5jdGlvbihlKXtyZXR1cm4gbnVsbD09ZT9bXTp6dChlLEJhKGUpKX0sanIud2l0aG91dD1hcyxqci53b3Jkcz0kYSxqci53cmFwPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGpzKHZuKHQpLGUpfSxqci54b3I9Y3MsanIueG9yQnk9bHMsanIueG9yV2l0aD11cyxqci56aXA9aHMsanIuemlwT2JqZWN0PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGRuKGV8fFtdLHR8fFtdLFFyKX0sanIuemlwT2JqZWN0RGVlcD1mdW5jdGlvbihlLHQpe3JldHVybiBkbihlfHxbXSx0fHxbXSxaaSl9LGpyLnppcFdpdGg9ZnMsanIuZW50cmllcz1GYSxqci5lbnRyaWVzSW49V2EsanIuZXh0ZW5kPVNhLGpyLmV4dGVuZFdpdGg9Q2EsY2MoanIsanIpLGpyLmFkZD1tYyxqci5hdHRlbXB0PVFhLGpyLmNhbWVsQ2FzZT1xYSxqci5jYXBpdGFsaXplPU5hLGpyLmNlaWw9YmMsanIuY2xhbXA9ZnVuY3Rpb24oZSx0LHIpe3JldHVybiByPT09biYmKHI9dCx0PW4pLHIhPT1uJiYocj0ocj1nYShyKSk9PXI/cjowKSx0IT09biYmKHQ9KHQ9Z2EodCkpPT10P3Q6MCksb2koZ2EoZSksdCxyKX0sanIuY2xvbmU9ZnVuY3Rpb24oZSl7cmV0dXJuIHNpKGUsNCl9LGpyLmNsb25lRGVlcD1mdW5jdGlvbihlKXtyZXR1cm4gc2koZSw1KX0sanIuY2xvbmVEZWVwV2l0aD1mdW5jdGlvbihlLHQpe3JldHVybiBzaShlLDUsdD0iZnVuY3Rpb24iPT10eXBlb2YgdD90Om4pfSxqci5jbG9uZVdpdGg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gc2koZSw0LHQ9ImZ1bmN0aW9uIj09dHlwZW9mIHQ/dDpuKX0sanIuY29uZm9ybXNUbz1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT10fHxhaShlLHQsT2EodCkpfSxqci5kZWJ1cnI9emEsanIuZGVmYXVsdFRvPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIG51bGw9PWV8fGUhPWU/dDplfSxqci5kaXZpZGU9U2MsanIuZW5kc1dpdGg9ZnVuY3Rpb24oZSx0LHIpe2U9bWEoZSksdD1hbih0KTt2YXIgaT1lLmxlbmd0aCxvPXI9cj09PW4/aTpvaShwYShyKSwwLGkpO3JldHVybihyLT10Lmxlbmd0aCk+PTAmJmUuc2xpY2UocixvKT09dH0sanIuZXE9VXMsanIuZXNjYXBlPWZ1bmN0aW9uKGUpe3JldHVybihlPW1hKGUpKSYmWS50ZXN0KGUpP2UucmVwbGFjZShWLFp0KTplfSxqci5lc2NhcGVSZWdFeHA9ZnVuY3Rpb24oZSl7cmV0dXJuKGU9bWEoZSkpJiZyZS50ZXN0KGUpP2UucmVwbGFjZSh0ZSwiXFwkJiIpOmV9LGpyLmV2ZXJ5PWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1LcyhlKT9TdDpmaTtyZXR1cm4gciYmeW8oZSx0LHIpJiYodD1uKSxpKGUsc28odCwzKSl9LGpyLmZpbmQ9Z3MsanIuZmluZEluZGV4PXpvLGpyLmZpbmRLZXk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gVHQoZSxzbyh0LDMpLHlpKX0sanIuZmluZExhc3Q9eXMsanIuZmluZExhc3RJbmRleD1Lbyxqci5maW5kTGFzdEtleT1mdW5jdGlvbihlLHQpe3JldHVybiBUdChlLHNvKHQsMyksbWkpfSxqci5mbG9vcj1DYyxqci5mb3JFYWNoPW1zLGpyLmZvckVhY2hSaWdodD1icyxqci5mb3JJbj1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT1lP2U6dmkoZSxzbyh0LDMpLEJhKX0sanIuZm9ySW5SaWdodD1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT1lP2U6Z2koZSxzbyh0LDMpLEJhKX0sanIuZm9yT3duPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUmJnlpKGUsc28odCwzKSl9LGpyLmZvck93blJpZ2h0PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUmJm1pKGUsc28odCwzKSl9LGpyLmdldD1BYSxqci5ndD1xcyxqci5ndGU9TnMsanIuaGFzPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIG51bGwhPWUmJl9vKGUsdCxFaSl9LGpyLmhhc0luPWthLGpyLmhlYWQ9R28sanIuaWRlbnRpdHk9bmMsanIuaW5jbHVkZXM9ZnVuY3Rpb24oZSx0LHIsaSl7ZT1HcyhlKT9lOlVhKGUpLHI9ciYmIWk/cGEocik6MDt2YXIgbj1lLmxlbmd0aDtyZXR1cm4gcjwwJiYocj12cihuK3IsMCkpLGNhKGUpP3I8PW4mJmUuaW5kZXhPZih0LHIpPi0xOiEhbiYmQnQoZSx0LHIpPi0xfSxqci5pbmRleE9mPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1udWxsPT1lPzA6ZS5sZW5ndGg7aWYoIWkpcmV0dXJuLTE7dmFyIG49bnVsbD09cj8wOnBhKHIpO3JldHVybiBuPDAmJihuPXZyKGkrbiwwKSksQnQoZSx0LG4pfSxqci5pblJhbmdlPWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gdD1kYSh0KSxyPT09bj8ocj10LHQ9MCk6cj1kYShyKSxmdW5jdGlvbihlLHQscil7cmV0dXJuIGU+PWdyKHQscikmJmU8dnIodCxyKX0oZT1nYShlKSx0LHIpfSxqci5pbnZva2U9VGEsanIuaXNBcmd1bWVudHM9enMsanIuaXNBcnJheT1Lcyxqci5pc0FycmF5QnVmZmVyPVZzLGpyLmlzQXJyYXlMaWtlPUdzLGpyLmlzQXJyYXlMaWtlT2JqZWN0PVlzLGpyLmlzQm9vbGVhbj1mdW5jdGlvbihlKXtyZXR1cm4hMD09PWV8fCExPT09ZXx8cmEoZSkmJndpKGUpPT1nfSxqci5pc0J1ZmZlcj1Ycyxqci5pc0RhdGU9WnMsanIuaXNFbGVtZW50PWZ1bmN0aW9uKGUpe3JldHVybiByYShlKSYmMT09PWUubm9kZVR5cGUmJiFvYShlKX0sanIuaXNFbXB0eT1mdW5jdGlvbihlKXtpZihudWxsPT1lKXJldHVybiEwO2lmKEdzKGUpJiYoS3MoZSl8fCJzdHJpbmciPT10eXBlb2YgZXx8ImZ1bmN0aW9uIj09dHlwZW9mIGUuc3BsaWNlfHxYcyhlKXx8dWEoZSl8fHpzKGUpKSlyZXR1cm4hZS5sZW5ndGg7dmFyIHQ9Zm8oZSk7aWYodD09Q3x8dD09QSlyZXR1cm4hZS5zaXplO2lmKENvKGUpKXJldHVybiFEaShlKS5sZW5ndGg7Zm9yKHZhciByIGluIGUpaWYoQmUuY2FsbChlLHIpKXJldHVybiExO3JldHVybiEwfSxqci5pc0VxdWFsPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIFJpKGUsdCl9LGpyLmlzRXF1YWxXaXRoPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT0ocj0iZnVuY3Rpb24iPT10eXBlb2Ygcj9yOm4pP3IoZSx0KTpuO3JldHVybiBpPT09bj9SaShlLHQsbixyKTohIWl9LGpyLmlzRXJyb3I9SnMsanIuaXNGaW5pdGU9ZnVuY3Rpb24oZSl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlJiZfcihlKX0sanIuaXNGdW5jdGlvbj0kcyxqci5pc0ludGVnZXI9UXMsanIuaXNMZW5ndGg9ZWEsanIuaXNNYXA9aWEsanIuaXNNYXRjaD1mdW5jdGlvbihlLHQpe3JldHVybiBlPT09dHx8VGkoZSx0LGNvKHQpKX0sanIuaXNNYXRjaFdpdGg9ZnVuY3Rpb24oZSx0LHIpe3JldHVybiByPSJmdW5jdGlvbiI9PXR5cGVvZiByP3I6bixUaShlLHQsY28odCkscil9LGpyLmlzTmFOPWZ1bmN0aW9uKGUpe3JldHVybiBuYShlKSYmZSE9K2V9LGpyLmlzTmF0aXZlPWZ1bmN0aW9uKGUpe2lmKFNvKGUpKXRocm93IG5ldyBTZSgiVW5zdXBwb3J0ZWQgY29yZS1qcyB1c2UuIFRyeSBodHRwczovL25wbXMuaW8vc2VhcmNoP3E9cG9ueWZpbGwuIik7cmV0dXJuIE9pKGUpfSxqci5pc05pbD1mdW5jdGlvbihlKXtyZXR1cm4gbnVsbD09ZX0sanIuaXNOdWxsPWZ1bmN0aW9uKGUpe3JldHVybiBudWxsPT09ZX0sanIuaXNOdW1iZXI9bmEsanIuaXNPYmplY3Q9dGEsanIuaXNPYmplY3RMaWtlPXJhLGpyLmlzUGxhaW5PYmplY3Q9b2EsanIuaXNSZWdFeHA9c2EsanIuaXNTYWZlSW50ZWdlcj1mdW5jdGlvbihlKXtyZXR1cm4gUXMoZSkmJmU+PS05MDA3MTk5MjU0NzQwOTkxJiZlPD1ofSxqci5pc1NldD1hYSxqci5pc1N0cmluZz1jYSxqci5pc1N5bWJvbD1sYSxqci5pc1R5cGVkQXJyYXk9dWEsanIuaXNVbmRlZmluZWQ9ZnVuY3Rpb24oZSl7cmV0dXJuIGU9PT1ufSxqci5pc1dlYWtNYXA9ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZmbyhlKT09Un0sanIuaXNXZWFrU2V0PWZ1bmN0aW9uKGUpe3JldHVybiByYShlKSYmIltvYmplY3QgV2Vha1NldF0iPT13aShlKX0sanIuam9pbj1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT1lPyIiOmRyLmNhbGwoZSx0KX0sanIua2ViYWJDYXNlPUthLGpyLmxhc3Q9Sm8sanIubGFzdEluZGV4T2Y9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtpZighaSlyZXR1cm4tMTt2YXIgbz1pO3JldHVybiByIT09biYmKG89KG89cGEocikpPDA/dnIoaStvLDApOmdyKG8saS0xKSksdD09dD9mdW5jdGlvbihlLHQscil7Zm9yKHZhciBpPXIrMTtpLS07KWlmKGVbaV09PT10KXJldHVybiBpO3JldHVybiBpfShlLHQsbyk6T3QoZSxQdCxvLCEwKX0sanIubG93ZXJDYXNlPVZhLGpyLmxvd2VyRmlyc3Q9R2EsanIubHQ9aGEsanIubHRlPWZhLGpyLm1heD1mdW5jdGlvbihlKXtyZXR1cm4gZSYmZS5sZW5ndGg/X2koZSxuYyxMaSk6bn0sanIubWF4Qnk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/X2koZSxzbyh0LDIpLExpKTpufSxqci5tZWFuPWZ1bmN0aW9uKGUpe3JldHVybiBJdChlLG5jKX0sanIubWVhbkJ5PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIEl0KGUsc28odCwyKSl9LGpyLm1pbj1mdW5jdGlvbihlKXtyZXR1cm4gZSYmZS5sZW5ndGg/X2koZSxuYyxQaSk6bn0sanIubWluQnk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/X2koZSxzbyh0LDIpLFBpKTpufSxqci5zdHViQXJyYXk9dmMsanIuc3R1YkZhbHNlPWdjLGpyLnN0dWJPYmplY3Q9ZnVuY3Rpb24oKXtyZXR1cm57fX0sanIuc3R1YlN0cmluZz1mdW5jdGlvbigpe3JldHVybiIifSxqci5zdHViVHJ1ZT1mdW5jdGlvbigpe3JldHVybiEwfSxqci5tdWx0aXBseT13Yyxqci5udGg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/V2koZSxwYSh0KSk6bn0sanIubm9Db25mbGljdD1mdW5jdGlvbigpe3JldHVybiBvdC5fPT09dGhpcyYmKG90Ll89amUpLHRoaXN9LGpyLm5vb3A9bGMsanIubm93PUFzLGpyLnBhZD1mdW5jdGlvbihlLHQscil7ZT1tYShlKTt2YXIgaT0odD1wYSh0KSk/bnIoZSk6MDtpZighdHx8aT49dClyZXR1cm4gZTt2YXIgbj0odC1pKS8yO3JldHVybiBxbih1cihuKSxyKStlK3FuKGxyKG4pLHIpfSxqci5wYWRFbmQ9ZnVuY3Rpb24oZSx0LHIpe2U9bWEoZSk7dmFyIGk9KHQ9cGEodCkpP25yKGUpOjA7cmV0dXJuIHQmJmk8dD9lK3FuKHQtaSxyKTplfSxqci5wYWRTdGFydD1mdW5jdGlvbihlLHQscil7ZT1tYShlKTt2YXIgaT0odD1wYSh0KSk/bnIoZSk6MDtyZXR1cm4gdCYmaTx0P3FuKHQtaSxyKStlOmV9LGpyLnBhcnNlSW50PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gcnx8bnVsbD09dD90PTA6dCYmKHQ9K3QpLG1yKG1hKGUpLnJlcGxhY2UoaWUsIiIpLHR8fDApfSxqci5yYW5kb209ZnVuY3Rpb24oZSx0LHIpe2lmKHImJiJib29sZWFuIiE9dHlwZW9mIHImJnlvKGUsdCxyKSYmKHQ9cj1uKSxyPT09biYmKCJib29sZWFuIj09dHlwZW9mIHQ/KHI9dCx0PW4pOiJib29sZWFuIj09dHlwZW9mIGUmJihyPWUsZT1uKSksZT09PW4mJnQ9PT1uPyhlPTAsdD0xKTooZT1kYShlKSx0PT09bj8odD1lLGU9MCk6dD1kYSh0KSksZT50KXt2YXIgaT1lO2U9dCx0PWl9aWYocnx8ZSUxfHx0JTEpe3ZhciBvPWJyKCk7cmV0dXJuIGdyKGUrbyoodC1lK3R0KCIxZS0iKygobysiIikubGVuZ3RoLTEpKSksdCl9cmV0dXJuIEtpKGUsdCl9LGpyLnJlZHVjZT1mdW5jdGlvbihlLHQscil7dmFyIGk9S3MoZSk/QXQ6RnQsbj1hcmd1bWVudHMubGVuZ3RoPDM7cmV0dXJuIGkoZSxzbyh0LDQpLHIsbix1aSl9LGpyLnJlZHVjZVJpZ2h0PWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1LcyhlKT9rdDpGdCxuPWFyZ3VtZW50cy5sZW5ndGg8MztyZXR1cm4gaShlLHNvKHQsNCkscixuLGhpKX0sanIucmVwZWF0PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gdD0ocj95byhlLHQscik6dD09PW4pPzE6cGEodCksVmkobWEoZSksdCl9LGpyLnJlcGxhY2U9ZnVuY3Rpb24oKXt2YXIgZT1hcmd1bWVudHMsdD1tYShlWzBdKTtyZXR1cm4gZS5sZW5ndGg8Mz90OnQucmVwbGFjZShlWzFdLGVbMl0pfSxqci5yZXN1bHQ9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPS0xLG89KHQ9Z24odCxlKSkubGVuZ3RoO2ZvcihvfHwobz0xLGU9bik7KytpPG87KXt2YXIgcz1udWxsPT1lP246ZVtqbyh0W2ldKV07cz09PW4mJihpPW8scz1yKSxlPSRzKHMpP3MuY2FsbChlKTpzfXJldHVybiBlfSxqci5yb3VuZD1MYyxqci5ydW5JbkNvbnRleHQ9ZSxqci5zYW1wbGU9ZnVuY3Rpb24oZSl7cmV0dXJuKEtzKGUpP1hyOllpKShlKX0sanIuc2l6ZT1mdW5jdGlvbihlKXtpZihudWxsPT1lKXJldHVybiAwO2lmKEdzKGUpKXJldHVybiBjYShlKT9ucihlKTplLmxlbmd0aDt2YXIgdD1mbyhlKTtyZXR1cm4gdD09Q3x8dD09QT9lLnNpemU6RGkoZSkubGVuZ3RofSxqci5zbmFrZUNhc2U9WWEsanIuc29tZT1mdW5jdGlvbihlLHQscil7dmFyIGk9S3MoZSk/TXQ6dG47cmV0dXJuIHImJnlvKGUsdCxyKSYmKHQ9biksaShlLHNvKHQsMykpfSxqci5zb3J0ZWRJbmRleD1mdW5jdGlvbihlLHQpe3JldHVybiBybihlLHQpfSxqci5zb3J0ZWRJbmRleEJ5PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gbm4oZSx0LHNvKHIsMikpfSxqci5zb3J0ZWRJbmRleE9mPWZ1bmN0aW9uKGUsdCl7dmFyIHI9bnVsbD09ZT8wOmUubGVuZ3RoO2lmKHIpe3ZhciBpPXJuKGUsdCk7aWYoaTxyJiZVcyhlW2ldLHQpKXJldHVybiBpfXJldHVybi0xfSxqci5zb3J0ZWRMYXN0SW5kZXg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gcm4oZSx0LCEwKX0sanIuc29ydGVkTGFzdEluZGV4Qnk9ZnVuY3Rpb24oZSx0LHIpe3JldHVybiBubihlLHQsc28ociwyKSwhMCl9LGpyLnNvcnRlZExhc3RJbmRleE9mPWZ1bmN0aW9uKGUsdCl7aWYobnVsbCE9ZSYmZS5sZW5ndGgpe3ZhciByPXJuKGUsdCwhMCktMTtpZihVcyhlW3JdLHQpKXJldHVybiByfXJldHVybi0xfSxqci5zdGFydENhc2U9WGEsanIuc3RhcnRzV2l0aD1mdW5jdGlvbihlLHQscil7cmV0dXJuIGU9bWEoZSkscj1udWxsPT1yPzA6b2kocGEociksMCxlLmxlbmd0aCksdD1hbih0KSxlLnNsaWNlKHIscit0Lmxlbmd0aCk9PXR9LGpyLnN1YnRyYWN0PUVjLGpyLnN1bT1mdW5jdGlvbihlKXtyZXR1cm4gZSYmZS5sZW5ndGg/V3QoZSxuYyk6MH0sanIuc3VtQnk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/V3QoZSxzbyh0LDIpKTowfSxqci50ZW1wbGF0ZT1mdW5jdGlvbihlLHQscil7dmFyIGk9anIudGVtcGxhdGVTZXR0aW5ncztyJiZ5byhlLHQscikmJih0PW4pLGU9bWEoZSksdD1DYSh7fSx0LGksWm4pO3ZhciBvLHMsYT1DYSh7fSx0LmltcG9ydHMsaS5pbXBvcnRzLFpuKSxjPU9hKGEpLGw9enQoYSxjKSx1PTAsaD10LmludGVycG9sYXRlfHxtZSxmPSJfX3AgKz0gJyIsXz1FZSgodC5lc2NhcGV8fG1lKS5zb3VyY2UrInwiK2guc291cmNlKyJ8IisoaD09PUo/aGU6bWUpLnNvdXJjZSsifCIrKHQuZXZhbHVhdGV8fG1lKS5zb3VyY2UrInwkIiwiZyIpLGQ9Ii8vIyBzb3VyY2VVUkw9IisoQmUuY2FsbCh0LCJzb3VyY2VVUkwiKT8odC5zb3VyY2VVUkwrIiIpLnJlcGxhY2UoL1xzL2csIiAiKToibG9kYXNoLnRlbXBsYXRlU291cmNlc1siKyArK0plKyJdIikrIlxuIjtlLnJlcGxhY2UoXywoZnVuY3Rpb24odCxyLGksbixhLGMpe3JldHVybiBpfHwoaT1uKSxmKz1lLnNsaWNlKHUsYykucmVwbGFjZShiZSxKdCksciYmKG89ITAsZis9IicgK1xuX19lKCIrcisiKSArXG4nIiksYSYmKHM9ITAsZis9Iic7XG4iK2ErIjtcbl9fcCArPSAnIiksaSYmKGYrPSInICtcbigoX190ID0gKCIraSsiKSkgPT0gbnVsbCA/ICcnIDogX190KSArXG4nIiksdT1jK3QubGVuZ3RoLHR9KSksZis9Iic7XG4iO3ZhciBwPUJlLmNhbGwodCwidmFyaWFibGUiKSYmdC52YXJpYWJsZTtpZihwKXtpZihsZS50ZXN0KHApKXRocm93IG5ldyBTZSgiSW52YWxpZCBgdmFyaWFibGVgIG9wdGlvbiBwYXNzZWQgaW50byBgXy50ZW1wbGF0ZWAiKX1lbHNlIGY9IndpdGggKG9iaikge1xuIitmKyJcbn1cbiI7Zj0ocz9mLnJlcGxhY2UocSwiIik6ZikucmVwbGFjZShOLCIkMSIpLnJlcGxhY2UoeiwiJDE7IiksZj0iZnVuY3Rpb24oIisocHx8Im9iaiIpKyIpIHtcbiIrKHA/IiI6Im9iaiB8fCAob2JqID0ge30pO1xuIikrInZhciBfX3QsIF9fcCA9ICcnIisobz8iLCBfX2UgPSBfLmVzY2FwZSI6IiIpKyhzPyIsIF9faiA9IEFycmF5LnByb3RvdHlwZS5qb2luO1xuZnVuY3Rpb24gcHJpbnQoKSB7IF9fcCArPSBfX2ouY2FsbChhcmd1bWVudHMsICcnKSB9XG4iOiI7XG4iKStmKyJyZXR1cm4gX19wXG59Ijt2YXIgdj1RYSgoZnVuY3Rpb24oKXtyZXR1cm4gQ2UoYyxkKyJyZXR1cm4gIitmKS5hcHBseShuLGwpfSkpO2lmKHYuc291cmNlPWYsSnModikpdGhyb3cgdjtyZXR1cm4gdn0sanIudGltZXM9ZnVuY3Rpb24oZSx0KXtpZigoZT1wYShlKSk8MXx8ZT5oKXJldHVybltdO3ZhciByPV8saT1ncihlLF8pO3Q9c28odCksZS09Xztmb3IodmFyIG49VXQoaSx0KTsrK3I8ZTspdChyKTtyZXR1cm4gbn0sanIudG9GaW5pdGU9ZGEsanIudG9JbnRlZ2VyPXBhLGpyLnRvTGVuZ3RoPXZhLGpyLnRvTG93ZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIG1hKGUpLnRvTG93ZXJDYXNlKCl9LGpyLnRvTnVtYmVyPWdhLGpyLnRvU2FmZUludGVnZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIGU/b2kocGEoZSksLTkwMDcxOTkyNTQ3NDA5OTEsaCk6MD09PWU/ZTowfSxqci50b1N0cmluZz1tYSxqci50b1VwcGVyPWZ1bmN0aW9uKGUpe3JldHVybiBtYShlKS50b1VwcGVyQ2FzZSgpfSxqci50cmltPWZ1bmN0aW9uKGUsdCxyKXtpZigoZT1tYShlKSkmJihyfHx0PT09bikpcmV0dXJuIHF0KGUpO2lmKCFlfHwhKHQ9YW4odCkpKXJldHVybiBlO3ZhciBpPW9yKGUpLG89b3IodCk7cmV0dXJuIG1uKGksVnQoaSxvKSxHdChpLG8pKzEpLmpvaW4oIiIpfSxqci50cmltRW5kPWZ1bmN0aW9uKGUsdCxyKXtpZigoZT1tYShlKSkmJihyfHx0PT09bikpcmV0dXJuIGUuc2xpY2UoMCxzcihlKSsxKTtpZighZXx8ISh0PWFuKHQpKSlyZXR1cm4gZTt2YXIgaT1vcihlKTtyZXR1cm4gbW4oaSwwLEd0KGksb3IodCkpKzEpLmpvaW4oIiIpfSxqci50cmltU3RhcnQ9ZnVuY3Rpb24oZSx0LHIpe2lmKChlPW1hKGUpKSYmKHJ8fHQ9PT1uKSlyZXR1cm4gZS5yZXBsYWNlKGllLCIiKTtpZighZXx8ISh0PWFuKHQpKSlyZXR1cm4gZTt2YXIgaT1vcihlKTtyZXR1cm4gbW4oaSxWdChpLG9yKHQpKSkuam9pbigiIil9LGpyLnRydW5jYXRlPWZ1bmN0aW9uKGUsdCl7dmFyIHI9MzAsaT0iLi4uIjtpZih0YSh0KSl7dmFyIG89InNlcGFyYXRvciJpbiB0P3Quc2VwYXJhdG9yOm87cj0ibGVuZ3RoImluIHQ/cGEodC5sZW5ndGgpOnIsaT0ib21pc3Npb24iaW4gdD9hbih0Lm9taXNzaW9uKTppfXZhciBzPShlPW1hKGUpKS5sZW5ndGg7aWYoJHQoZSkpe3ZhciBhPW9yKGUpO3M9YS5sZW5ndGh9aWYocj49cylyZXR1cm4gZTt2YXIgYz1yLW5yKGkpO2lmKGM8MSlyZXR1cm4gaTt2YXIgbD1hP21uKGEsMCxjKS5qb2luKCIiKTplLnNsaWNlKDAsYyk7aWYobz09PW4pcmV0dXJuIGwraTtpZihhJiYoYys9bC5sZW5ndGgtYyksc2Eobykpe2lmKGUuc2xpY2UoYykuc2VhcmNoKG8pKXt2YXIgdSxoPWw7Zm9yKG8uZ2xvYmFsfHwobz1FZShvLnNvdXJjZSxtYShmZS5leGVjKG8pKSsiZyIpKSxvLmxhc3RJbmRleD0wO3U9by5leGVjKGgpOyl2YXIgZj11LmluZGV4O2w9bC5zbGljZSgwLGY9PT1uP2M6Zil9fWVsc2UgaWYoZS5pbmRleE9mKGFuKG8pLGMpIT1jKXt2YXIgXz1sLmxhc3RJbmRleE9mKG8pO18+LTEmJihsPWwuc2xpY2UoMCxfKSl9cmV0dXJuIGwraX0sanIudW5lc2NhcGU9ZnVuY3Rpb24oZSl7cmV0dXJuKGU9bWEoZSkpJiZHLnRlc3QoZSk/ZS5yZXBsYWNlKEssYXIpOmV9LGpyLnVuaXF1ZUlkPWZ1bmN0aW9uKGUpe3ZhciB0PSsrRGU7cmV0dXJuIG1hKGUpK3R9LGpyLnVwcGVyQ2FzZT1aYSxqci51cHBlckZpcnN0PUphLGpyLmVhY2g9bXMsanIuZWFjaFJpZ2h0PWJzLGpyLmZpcnN0PUdvLGNjKGpyLCh5Yz17fSx5aShqciwoZnVuY3Rpb24oZSx0KXtCZS5jYWxsKGpyLnByb3RvdHlwZSx0KXx8KHljW3RdPWUpfSkpLHljKSx7Y2hhaW46ITF9KSxqci5WRVJTSU9OPSI0LjE3LjIxIixtdChbImJpbmQiLCJiaW5kS2V5IiwiY3VycnkiLCJjdXJyeVJpZ2h0IiwicGFydGlhbCIsInBhcnRpYWxSaWdodCJdLChmdW5jdGlvbihlKXtqcltlXS5wbGFjZWhvbGRlcj1qcn0pKSxtdChbImRyb3AiLCJ0YWtlIl0sKGZ1bmN0aW9uKGUsdCl7cXIucHJvdG90eXBlW2VdPWZ1bmN0aW9uKHIpe3I9cj09PW4/MTp2cihwYShyKSwwKTt2YXIgaT10aGlzLl9fZmlsdGVyZWRfXyYmIXQ/bmV3IHFyKHRoaXMpOnRoaXMuY2xvbmUoKTtyZXR1cm4gaS5fX2ZpbHRlcmVkX18/aS5fX3Rha2VDb3VudF9fPWdyKHIsaS5fX3Rha2VDb3VudF9fKTppLl9fdmlld3NfXy5wdXNoKHtzaXplOmdyKHIsXyksdHlwZTplKyhpLl9fZGlyX188MD8iUmlnaHQiOiIiKX0pLGl9LHFyLnByb3RvdHlwZVtlKyJSaWdodCJdPWZ1bmN0aW9uKHQpe3JldHVybiB0aGlzLnJldmVyc2UoKVtlXSh0KS5yZXZlcnNlKCl9fSkpLG10KFsiZmlsdGVyIiwibWFwIiwidGFrZVdoaWxlIl0sKGZ1bmN0aW9uKGUsdCl7dmFyIHI9dCsxLGk9MT09cnx8Mz09cjtxci5wcm90b3R5cGVbZV09ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5jbG9uZSgpO3JldHVybiB0Ll9faXRlcmF0ZWVzX18ucHVzaCh7aXRlcmF0ZWU6c28oZSwzKSx0eXBlOnJ9KSx0Ll9fZmlsdGVyZWRfXz10Ll9fZmlsdGVyZWRfX3x8aSx0fX0pKSxtdChbImhlYWQiLCJsYXN0Il0sKGZ1bmN0aW9uKGUsdCl7dmFyIHI9InRha2UiKyh0PyJSaWdodCI6IiIpO3FyLnByb3RvdHlwZVtlXT1mdW5jdGlvbigpe3JldHVybiB0aGlzW3JdKDEpLnZhbHVlKClbMF19fSkpLG10KFsiaW5pdGlhbCIsInRhaWwiXSwoZnVuY3Rpb24oZSx0KXt2YXIgcj0iZHJvcCIrKHQ/IiI6IlJpZ2h0Iik7cXIucHJvdG90eXBlW2VdPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX19maWx0ZXJlZF9fP25ldyBxcih0aGlzKTp0aGlzW3JdKDEpfX0pKSxxci5wcm90b3R5cGUuY29tcGFjdD1mdW5jdGlvbigpe3JldHVybiB0aGlzLmZpbHRlcihuYyl9LHFyLnByb3RvdHlwZS5maW5kPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLmZpbHRlcihlKS5oZWFkKCl9LHFyLnByb3RvdHlwZS5maW5kTGFzdD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5yZXZlcnNlKCkuZmluZChlKX0scXIucHJvdG90eXBlLmludm9rZU1hcD1HaSgoZnVuY3Rpb24oZSx0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgZT9uZXcgcXIodGhpcyk6dGhpcy5tYXAoKGZ1bmN0aW9uKHIpe3JldHVybiBraShyLGUsdCl9KSl9KSkscXIucHJvdG90eXBlLnJlamVjdD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5maWx0ZXIoSXMoc28oZSkpKX0scXIucHJvdG90eXBlLnNsaWNlPWZ1bmN0aW9uKGUsdCl7ZT1wYShlKTt2YXIgcj10aGlzO3JldHVybiByLl9fZmlsdGVyZWRfXyYmKGU+MHx8dDwwKT9uZXcgcXIocik6KGU8MD9yPXIudGFrZVJpZ2h0KC1lKTplJiYocj1yLmRyb3AoZSkpLHQhPT1uJiYocj0odD1wYSh0KSk8MD9yLmRyb3BSaWdodCgtdCk6ci50YWtlKHQtZSkpLHIpfSxxci5wcm90b3R5cGUudGFrZVJpZ2h0V2hpbGU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMucmV2ZXJzZSgpLnRha2VXaGlsZShlKS5yZXZlcnNlKCl9LHFyLnByb3RvdHlwZS50b0FycmF5PWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMudGFrZShfKX0seWkocXIucHJvdG90eXBlLChmdW5jdGlvbihlLHQpe3ZhciByPS9eKD86ZmlsdGVyfGZpbmR8bWFwfHJlamVjdCl8V2hpbGUkLy50ZXN0KHQpLGk9L14oPzpoZWFkfGxhc3QpJC8udGVzdCh0KSxvPWpyW2k/InRha2UiKygibGFzdCI9PXQ/IlJpZ2h0IjoiIik6dF0scz1pfHwvXmZpbmQvLnRlc3QodCk7byYmKGpyLnByb3RvdHlwZVt0XT1mdW5jdGlvbigpe3ZhciB0PXRoaXMuX193cmFwcGVkX18sYT1pP1sxXTphcmd1bWVudHMsYz10IGluc3RhbmNlb2YgcXIsbD1hWzBdLHU9Y3x8S3ModCksaD1mdW5jdGlvbihlKXt2YXIgdD1vLmFwcGx5KGpyLHh0KFtlXSxhKSk7cmV0dXJuIGkmJmY/dFswXTp0fTt1JiZyJiYiZnVuY3Rpb24iPT10eXBlb2YgbCYmMSE9bC5sZW5ndGgmJihjPXU9ITEpO3ZhciBmPXRoaXMuX19jaGFpbl9fLF89ISF0aGlzLl9fYWN0aW9uc19fLmxlbmd0aCxkPXMmJiFmLHA9YyYmIV87aWYoIXMmJnUpe3Q9cD90Om5ldyBxcih0aGlzKTt2YXIgdj1lLmFwcGx5KHQsYSk7cmV0dXJuIHYuX19hY3Rpb25zX18ucHVzaCh7ZnVuYzpkcyxhcmdzOltoXSx0aGlzQXJnOm59KSxuZXcgVXIodixmKX1yZXR1cm4gZCYmcD9lLmFwcGx5KHRoaXMsYSk6KHY9dGhpcy50aHJ1KGgpLGQ/aT92LnZhbHVlKClbMF06di52YWx1ZSgpOnYpfSl9KSksbXQoWyJwb3AiLCJwdXNoIiwic2hpZnQiLCJzb3J0Iiwic3BsaWNlIiwidW5zaGlmdCJdLChmdW5jdGlvbihlKXt2YXIgdD1rZVtlXSxyPS9eKD86cHVzaHxzb3J0fHVuc2hpZnQpJC8udGVzdChlKT8idGFwIjoidGhydSIsaT0vXig/OnBvcHxzaGlmdCkkLy50ZXN0KGUpO2pyLnByb3RvdHlwZVtlXT1mdW5jdGlvbigpe3ZhciBlPWFyZ3VtZW50cztpZihpJiYhdGhpcy5fX2NoYWluX18pe3ZhciBuPXRoaXMudmFsdWUoKTtyZXR1cm4gdC5hcHBseShLcyhuKT9uOltdLGUpfXJldHVybiB0aGlzW3JdKChmdW5jdGlvbihyKXtyZXR1cm4gdC5hcHBseShLcyhyKT9yOltdLGUpfSkpfX0pKSx5aShxci5wcm90b3R5cGUsKGZ1bmN0aW9uKGUsdCl7dmFyIHI9anJbdF07aWYocil7dmFyIGk9ci5uYW1lKyIiO0JlLmNhbGwoTXIsaSl8fChNcltpXT1bXSksTXJbaV0ucHVzaCh7bmFtZTp0LGZ1bmM6cn0pfX0pKSxNcltqbihuLDIpLm5hbWVdPVt7bmFtZToid3JhcHBlciIsZnVuYzpufV0scXIucHJvdG90eXBlLmNsb25lPWZ1bmN0aW9uKCl7dmFyIGU9bmV3IHFyKHRoaXMuX193cmFwcGVkX18pO3JldHVybiBlLl9fYWN0aW9uc19fPUFuKHRoaXMuX19hY3Rpb25zX18pLGUuX19kaXJfXz10aGlzLl9fZGlyX18sZS5fX2ZpbHRlcmVkX189dGhpcy5fX2ZpbHRlcmVkX18sZS5fX2l0ZXJhdGVlc19fPUFuKHRoaXMuX19pdGVyYXRlZXNfXyksZS5fX3Rha2VDb3VudF9fPXRoaXMuX190YWtlQ291bnRfXyxlLl9fdmlld3NfXz1Bbih0aGlzLl9fdmlld3NfXyksZX0scXIucHJvdG90eXBlLnJldmVyc2U9ZnVuY3Rpb24oKXtpZih0aGlzLl9fZmlsdGVyZWRfXyl7dmFyIGU9bmV3IHFyKHRoaXMpO2UuX19kaXJfXz0tMSxlLl9fZmlsdGVyZWRfXz0hMH1lbHNlKGU9dGhpcy5jbG9uZSgpKS5fX2Rpcl9fKj0tMTtyZXR1cm4gZX0scXIucHJvdG90eXBlLnZhbHVlPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5fX3dyYXBwZWRfXy52YWx1ZSgpLHQ9dGhpcy5fX2Rpcl9fLHI9S3MoZSksaT10PDAsbj1yP2UubGVuZ3RoOjAsbz1mdW5jdGlvbihlLHQscil7Zm9yKHZhciBpPS0xLG49ci5sZW5ndGg7KytpPG47KXt2YXIgbz1yW2ldLHM9by5zaXplO3N3aXRjaChvLnR5cGUpe2Nhc2UiZHJvcCI6ZSs9czticmVhaztjYXNlImRyb3BSaWdodCI6dC09czticmVhaztjYXNlInRha2UiOnQ9Z3IodCxlK3MpO2JyZWFrO2Nhc2UidGFrZVJpZ2h0IjplPXZyKGUsdC1zKX19cmV0dXJue3N0YXJ0OmUsZW5kOnR9fSgwLG4sdGhpcy5fX3ZpZXdzX18pLHM9by5zdGFydCxhPW8uZW5kLGM9YS1zLGw9aT9hOnMtMSx1PXRoaXMuX19pdGVyYXRlZXNfXyxoPXUubGVuZ3RoLGY9MCxfPWdyKGMsdGhpcy5fX3Rha2VDb3VudF9fKTtpZighcnx8IWkmJm49PWMmJl89PWMpcmV0dXJuIGZuKGUsdGhpcy5fX2FjdGlvbnNfXyk7dmFyIGQ9W107ZTpmb3IoO2MtLSYmZjxfOyl7Zm9yKHZhciBwPS0xLHY9ZVtsKz10XTsrK3A8aDspe3ZhciBnPXVbcF0seT1nLml0ZXJhdGVlLG09Zy50eXBlLGI9eSh2KTtpZigyPT1tKXY9YjtlbHNlIGlmKCFiKXtpZigxPT1tKWNvbnRpbnVlIGU7YnJlYWsgZX19ZFtmKytdPXZ9cmV0dXJuIGR9LGpyLnByb3RvdHlwZS5hdD1wcyxqci5wcm90b3R5cGUuY2hhaW49ZnVuY3Rpb24oKXtyZXR1cm4gX3ModGhpcyl9LGpyLnByb3RvdHlwZS5jb21taXQ9ZnVuY3Rpb24oKXtyZXR1cm4gbmV3IFVyKHRoaXMudmFsdWUoKSx0aGlzLl9fY2hhaW5fXyl9LGpyLnByb3RvdHlwZS5uZXh0PWZ1bmN0aW9uKCl7dGhpcy5fX3ZhbHVlc19fPT09biYmKHRoaXMuX192YWx1ZXNfXz1fYSh0aGlzLnZhbHVlKCkpKTt2YXIgZT10aGlzLl9faW5kZXhfXz49dGhpcy5fX3ZhbHVlc19fLmxlbmd0aDtyZXR1cm57ZG9uZTplLHZhbHVlOmU/bjp0aGlzLl9fdmFsdWVzX19bdGhpcy5fX2luZGV4X18rK119fSxqci5wcm90b3R5cGUucGxhbnQ9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0LHI9dGhpcztyIGluc3RhbmNlb2YgV3I7KXt2YXIgaT1XbyhyKTtpLl9faW5kZXhfXz0wLGkuX192YWx1ZXNfXz1uLHQ/by5fX3dyYXBwZWRfXz1pOnQ9aTt2YXIgbz1pO3I9ci5fX3dyYXBwZWRfX31yZXR1cm4gby5fX3dyYXBwZWRfXz1lLHR9LGpyLnByb3RvdHlwZS5yZXZlcnNlPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5fX3dyYXBwZWRfXztpZihlIGluc3RhbmNlb2YgcXIpe3ZhciB0PWU7cmV0dXJuIHRoaXMuX19hY3Rpb25zX18ubGVuZ3RoJiYodD1uZXcgcXIodGhpcykpLCh0PXQucmV2ZXJzZSgpKS5fX2FjdGlvbnNfXy5wdXNoKHtmdW5jOmRzLGFyZ3M6W3RzXSx0aGlzQXJnOm59KSxuZXcgVXIodCx0aGlzLl9fY2hhaW5fXyl9cmV0dXJuIHRoaXMudGhydSh0cyl9LGpyLnByb3RvdHlwZS50b0pTT049anIucHJvdG90eXBlLnZhbHVlT2Y9anIucHJvdG90eXBlLnZhbHVlPWZ1bmN0aW9uKCl7cmV0dXJuIGZuKHRoaXMuX193cmFwcGVkX18sdGhpcy5fX2FjdGlvbnNfXyl9LGpyLnByb3RvdHlwZS5maXJzdD1qci5wcm90b3R5cGUuaGVhZCxzdCYmKGpyLnByb3RvdHlwZVtzdF09ZnVuY3Rpb24oKXtyZXR1cm4gdGhpc30pLGpyfSgpO290Ll89Y3IsKGk9ZnVuY3Rpb24oKXtyZXR1cm4gY3J9LmNhbGwodCxyLHQsZSkpPT09bnx8KGUuZXhwb3J0cz1pKX0uY2FsbCh0aGlzKX0sMzc5OmU9PnsidXNlIHN0cmljdCI7dmFyIHQ9W107ZnVuY3Rpb24gcihlKXtmb3IodmFyIHI9LTEsaT0wO2k8dC5sZW5ndGg7aSsrKWlmKHRbaV0uaWRlbnRpZmllcj09PWUpe3I9aTticmVha31yZXR1cm4gcn1mdW5jdGlvbiBpKGUsaSl7Zm9yKHZhciBvPXt9LHM9W10sYT0wO2E8ZS5sZW5ndGg7YSsrKXt2YXIgYz1lW2FdLGw9aS5iYXNlP2NbMF0raS5iYXNlOmNbMF0sdT1vW2xdfHwwLGg9IiIuY29uY2F0KGwsIiAiKS5jb25jYXQodSk7b1tsXT11KzE7dmFyIGY9cihoKSxfPXtjc3M6Y1sxXSxtZWRpYTpjWzJdLHNvdXJjZU1hcDpjWzNdLHN1cHBvcnRzOmNbNF0sbGF5ZXI6Y1s1XX07aWYoLTEhPT1mKXRbZl0ucmVmZXJlbmNlcysrLHRbZl0udXBkYXRlcihfKTtlbHNle3ZhciBkPW4oXyxpKTtpLmJ5SW5kZXg9YSx0LnNwbGljZShhLDAse2lkZW50aWZpZXI6aCx1cGRhdGVyOmQscmVmZXJlbmNlczoxfSl9cy5wdXNoKGgpfXJldHVybiBzfWZ1bmN0aW9uIG4oZSx0KXt2YXIgcj10LmRvbUFQSSh0KTtyZXR1cm4gci51cGRhdGUoZSksZnVuY3Rpb24odCl7aWYodCl7aWYodC5jc3M9PT1lLmNzcyYmdC5tZWRpYT09PWUubWVkaWEmJnQuc291cmNlTWFwPT09ZS5zb3VyY2VNYXAmJnQuc3VwcG9ydHM9PT1lLnN1cHBvcnRzJiZ0LmxheWVyPT09ZS5sYXllcilyZXR1cm47ci51cGRhdGUoZT10KX1lbHNlIHIucmVtb3ZlKCl9fWUuZXhwb3J0cz1mdW5jdGlvbihlLG4pe3ZhciBvPWkoZT1lfHxbXSxuPW58fHt9KTtyZXR1cm4gZnVuY3Rpb24oZSl7ZT1lfHxbXTtmb3IodmFyIHM9MDtzPG8ubGVuZ3RoO3MrKyl7dmFyIGE9cihvW3NdKTt0W2FdLnJlZmVyZW5jZXMtLX1mb3IodmFyIGM9aShlLG4pLGw9MDtsPG8ubGVuZ3RoO2wrKyl7dmFyIHU9cihvW2xdKTswPT09dFt1XS5yZWZlcmVuY2VzJiYodFt1XS51cGRhdGVyKCksdC5zcGxpY2UodSwxKSl9bz1jfX19LDU2OTplPT57InVzZSBzdHJpY3QiO3ZhciB0PXt9O2UuZXhwb3J0cz1mdW5jdGlvbihlLHIpe3ZhciBpPWZ1bmN0aW9uKGUpe2lmKHZvaWQgMD09PXRbZV0pe3ZhciByPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoZSk7aWYod2luZG93LkhUTUxJRnJhbWVFbGVtZW50JiZyIGluc3RhbmNlb2Ygd2luZG93LkhUTUxJRnJhbWVFbGVtZW50KXRyeXtyPXIuY29udGVudERvY3VtZW50LmhlYWR9Y2F0Y2goZSl7cj1udWxsfXRbZV09cn1yZXR1cm4gdFtlXX0oZSk7aWYoIWkpdGhyb3cgbmV3IEVycm9yKCJDb3VsZG4ndCBmaW5kIGEgc3R5bGUgdGFyZ2V0LiBUaGlzIHByb2JhYmx5IG1lYW5zIHRoYXQgdGhlIHZhbHVlIGZvciB0aGUgJ2luc2VydCcgcGFyYW1ldGVyIGlzIGludmFsaWQuIik7aS5hcHBlbmRDaGlsZChyKX19LDIxNjplPT57InVzZSBzdHJpY3QiO2UuZXhwb3J0cz1mdW5jdGlvbihlKXt2YXIgdD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzdHlsZSIpO3JldHVybiBlLnNldEF0dHJpYnV0ZXModCxlLmF0dHJpYnV0ZXMpLGUuaW5zZXJ0KHQsZS5vcHRpb25zKSx0fX0sNTY1OihlLHQscik9PnsidXNlIHN0cmljdCI7ZS5leHBvcnRzPWZ1bmN0aW9uKGUpe3ZhciB0PXIubmM7dCYmZS5zZXRBdHRyaWJ1dGUoIm5vbmNlIix0KX19LDc5NTplPT57InVzZSBzdHJpY3QiO2UuZXhwb3J0cz1mdW5jdGlvbihlKXt2YXIgdD1lLmluc2VydFN0eWxlRWxlbWVudChlKTtyZXR1cm57dXBkYXRlOmZ1bmN0aW9uKHIpeyFmdW5jdGlvbihlLHQscil7dmFyIGk9IiI7ci5zdXBwb3J0cyYmKGkrPSJAc3VwcG9ydHMgKCIuY29uY2F0KHIuc3VwcG9ydHMsIikgeyIpKSxyLm1lZGlhJiYoaSs9IkBtZWRpYSAiLmNvbmNhdChyLm1lZGlhLCIgeyIpKTt2YXIgbj12b2lkIDAhPT1yLmxheWVyO24mJihpKz0iQGxheWVyIi5jb25jYXQoci5sYXllci5sZW5ndGg+MD8iICIuY29uY2F0KHIubGF5ZXIpOiIiLCIgeyIpKSxpKz1yLmNzcyxuJiYoaSs9In0iKSxyLm1lZGlhJiYoaSs9In0iKSxyLnN1cHBvcnRzJiYoaSs9In0iKTt2YXIgbz1yLnNvdXJjZU1hcDtvJiYidW5kZWZpbmVkIiE9dHlwZW9mIGJ0b2EmJihpKz0iXG4vKiMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCIuY29uY2F0KGJ0b2EodW5lc2NhcGUoZW5jb2RlVVJJQ29tcG9uZW50KEpTT04uc3RyaW5naWZ5KG8pKSkpLCIgKi8iKSksdC5zdHlsZVRhZ1RyYW5zZm9ybShpLGUsdC5vcHRpb25zKX0odCxlLHIpfSxyZW1vdmU6ZnVuY3Rpb24oKXshZnVuY3Rpb24oZSl7aWYobnVsbD09PWUucGFyZW50Tm9kZSlyZXR1cm4hMTtlLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoZSl9KHQpfX19fSw1ODk6ZT0+eyJ1c2Ugc3RyaWN0IjtlLmV4cG9ydHM9ZnVuY3Rpb24oZSx0KXtpZih0LnN0eWxlU2hlZXQpdC5zdHlsZVNoZWV0LmNzc1RleHQ9ZTtlbHNle2Zvcig7dC5maXJzdENoaWxkOyl0LnJlbW92ZUNoaWxkKHQuZmlyc3RDaGlsZCk7dC5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShlKSl9fX0sNjE3OmU9PntzZWxmLGUuZXhwb3J0cz0oKCk9PnsidXNlIHN0cmljdCI7dmFyIGU9ezc3NTooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkZpdEFkZG9uPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt9cmV0dXJuIGUucHJvdG90eXBlLmFjdGl2YXRlPWZ1bmN0aW9uKGUpe3RoaXMuX3Rlcm1pbmFsPWV9LGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt9LGUucHJvdG90eXBlLmZpdD1mdW5jdGlvbigpe3ZhciBlPXRoaXMucHJvcG9zZURpbWVuc2lvbnMoKTtpZihlJiZ0aGlzLl90ZXJtaW5hbCl7dmFyIHQ9dGhpcy5fdGVybWluYWwuX2NvcmU7dGhpcy5fdGVybWluYWwucm93cz09PWUucm93cyYmdGhpcy5fdGVybWluYWwuY29scz09PWUuY29sc3x8KHQuX3JlbmRlclNlcnZpY2UuY2xlYXIoKSx0aGlzLl90ZXJtaW5hbC5yZXNpemUoZS5jb2xzLGUucm93cykpfX0sZS5wcm90b3R5cGUucHJvcG9zZURpbWVuc2lvbnM9ZnVuY3Rpb24oKXtpZih0aGlzLl90ZXJtaW5hbCYmdGhpcy5fdGVybWluYWwuZWxlbWVudCYmdGhpcy5fdGVybWluYWwuZWxlbWVudC5wYXJlbnRFbGVtZW50KXt2YXIgZT10aGlzLl90ZXJtaW5hbC5fY29yZTtpZigwIT09ZS5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLmFjdHVhbENlbGxXaWR0aCYmMCE9PWUuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KXt2YXIgdD13aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZSh0aGlzLl90ZXJtaW5hbC5lbGVtZW50LnBhcmVudEVsZW1lbnQpLHI9cGFyc2VJbnQodC5nZXRQcm9wZXJ0eVZhbHVlKCJoZWlnaHQiKSksaT1NYXRoLm1heCgwLHBhcnNlSW50KHQuZ2V0UHJvcGVydHlWYWx1ZSgid2lkdGgiKSkpLG49d2luZG93LmdldENvbXB1dGVkU3R5bGUodGhpcy5fdGVybWluYWwuZWxlbWVudCksbz1yLShwYXJzZUludChuLmdldFByb3BlcnR5VmFsdWUoInBhZGRpbmctdG9wIikpK3BhcnNlSW50KG4uZ2V0UHJvcGVydHlWYWx1ZSgicGFkZGluZy1ib3R0b20iKSkpLHM9aS0ocGFyc2VJbnQobi5nZXRQcm9wZXJ0eVZhbHVlKCJwYWRkaW5nLXJpZ2h0IikpK3BhcnNlSW50KG4uZ2V0UHJvcGVydHlWYWx1ZSgicGFkZGluZy1sZWZ0IikpKS1lLnZpZXdwb3J0LnNjcm9sbEJhcldpZHRoO3JldHVybntjb2xzOk1hdGgubWF4KDIsTWF0aC5mbG9vcihzL2UuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsV2lkdGgpKSxyb3dzOk1hdGgubWF4KDEsTWF0aC5mbG9vcihvL2UuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KSl9fX19LGV9KCk7dC5GaXRBZGRvbj1yfX0sdD17fTtyZXR1cm4gZnVuY3Rpb24gcihpKXtpZih0W2ldKXJldHVybiB0W2ldLmV4cG9ydHM7dmFyIG49dFtpXT17ZXhwb3J0czp7fX07cmV0dXJuIGVbaV0obixuLmV4cG9ydHMsciksbi5leHBvcnRzfSg3NzUpfSkoKX0sMzIwOmU9PntzZWxmLGUuZXhwb3J0cz0oKCk9PnsidXNlIHN0cmljdCI7dmFyIGU9ezQ1Njc6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQWNjZXNzaWJpbGl0eU1hbmFnZXI9dm9pZCAwO3ZhciBvPXIoOTA0Mikscz1yKDYxMTQpLGE9cig5OTI0KSxjPXIoMzY1NiksbD1yKDg0NCksdT1yKDU1OTYpLGg9cig5NjMxKSxmPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyKXt2YXIgaT1lLmNhbGwodGhpcyl8fHRoaXM7aS5fdGVybWluYWw9dCxpLl9yZW5kZXJTZXJ2aWNlPXIsaS5fbGl2ZVJlZ2lvbkxpbmVDb3VudD0wLGkuX2NoYXJzVG9Db25zdW1lPVtdLGkuX2NoYXJzVG9Bbm5vdW5jZT0iIixpLl9hY2Nlc3NpYmlsaXR5VHJlZVJvb3Q9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2IiksaS5fYWNjZXNzaWJpbGl0eVRyZWVSb290LnNldEF0dHJpYnV0ZSgicm9sZSIsImRvY3VtZW50IiksaS5fYWNjZXNzaWJpbGl0eVRyZWVSb290LmNsYXNzTGlzdC5hZGQoInh0ZXJtLWFjY2Vzc2liaWxpdHkiKSxpLl9hY2Nlc3NpYmlsaXR5VHJlZVJvb3QudGFiSW5kZXg9MCxpLl9yb3dDb250YWluZXI9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2IiksaS5fcm93Q29udGFpbmVyLnNldEF0dHJpYnV0ZSgicm9sZSIsImxpc3QiKSxpLl9yb3dDb250YWluZXIuY2xhc3NMaXN0LmFkZCgieHRlcm0tYWNjZXNzaWJpbGl0eS10cmVlIiksaS5fcm93RWxlbWVudHM9W107Zm9yKHZhciBuPTA7bjxpLl90ZXJtaW5hbC5yb3dzO24rKylpLl9yb3dFbGVtZW50c1tuXT1pLl9jcmVhdGVBY2Nlc3NpYmlsaXR5VHJlZU5vZGUoKSxpLl9yb3dDb250YWluZXIuYXBwZW5kQ2hpbGQoaS5fcm93RWxlbWVudHNbbl0pO2lmKGkuX3RvcEJvdW5kYXJ5Rm9jdXNMaXN0ZW5lcj1mdW5jdGlvbihlKXtyZXR1cm4gaS5fb25Cb3VuZGFyeUZvY3VzKGUsMCl9LGkuX2JvdHRvbUJvdW5kYXJ5Rm9jdXNMaXN0ZW5lcj1mdW5jdGlvbihlKXtyZXR1cm4gaS5fb25Cb3VuZGFyeUZvY3VzKGUsMSl9LGkuX3Jvd0VsZW1lbnRzWzBdLmFkZEV2ZW50TGlzdGVuZXIoImZvY3VzIixpLl90b3BCb3VuZGFyeUZvY3VzTGlzdGVuZXIpLGkuX3Jvd0VsZW1lbnRzW2kuX3Jvd0VsZW1lbnRzLmxlbmd0aC0xXS5hZGRFdmVudExpc3RlbmVyKCJmb2N1cyIsaS5fYm90dG9tQm91bmRhcnlGb2N1c0xpc3RlbmVyKSxpLl9yZWZyZXNoUm93c0RpbWVuc2lvbnMoKSxpLl9hY2Nlc3NpYmlsaXR5VHJlZVJvb3QuYXBwZW5kQ2hpbGQoaS5fcm93Q29udGFpbmVyKSxpLl9yZW5kZXJSb3dzRGVib3VuY2VyPW5ldyBhLlRpbWVCYXNlZERlYm91bmNlcihpLl9yZW5kZXJSb3dzLmJpbmQoaSkpLGkuX3JlZnJlc2hSb3dzKCksaS5fbGl2ZVJlZ2lvbj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKSxpLl9saXZlUmVnaW9uLmNsYXNzTGlzdC5hZGQoImxpdmUtcmVnaW9uIiksaS5fbGl2ZVJlZ2lvbi5zZXRBdHRyaWJ1dGUoImFyaWEtbGl2ZSIsImFzc2VydGl2ZSIpLGkuX2FjY2Vzc2liaWxpdHlUcmVlUm9vdC5hcHBlbmRDaGlsZChpLl9saXZlUmVnaW9uKSwhaS5fdGVybWluYWwuZWxlbWVudCl0aHJvdyBuZXcgRXJyb3IoIkNhbm5vdCBlbmFibGUgYWNjZXNzaWJpbGl0eSBiZWZvcmUgVGVybWluYWwub3BlbiIpO3JldHVybiBpLl90ZXJtaW5hbC5lbGVtZW50Lmluc2VydEFkamFjZW50RWxlbWVudCgiYWZ0ZXJiZWdpbiIsaS5fYWNjZXNzaWJpbGl0eVRyZWVSb290KSxpLnJlZ2lzdGVyKGkuX3JlbmRlclJvd3NEZWJvdW5jZXIpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25SZXNpemUoKGZ1bmN0aW9uKGUpe3JldHVybiBpLl9vblJlc2l6ZShlLnJvd3MpfSkpKSxpLnJlZ2lzdGVyKGkuX3Rlcm1pbmFsLm9uUmVuZGVyKChmdW5jdGlvbihlKXtyZXR1cm4gaS5fcmVmcmVzaFJvd3MoZS5zdGFydCxlLmVuZCl9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25TY3JvbGwoKGZ1bmN0aW9uKCl7cmV0dXJuIGkuX3JlZnJlc2hSb3dzKCl9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25BMTF5Q2hhcigoZnVuY3Rpb24oZSl7cmV0dXJuIGkuX29uQ2hhcihlKX0pKSksaS5yZWdpc3RlcihpLl90ZXJtaW5hbC5vbkxpbmVGZWVkKChmdW5jdGlvbigpe3JldHVybiBpLl9vbkNoYXIoIlxuIil9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25BMTF5VGFiKChmdW5jdGlvbihlKXtyZXR1cm4gaS5fb25UYWIoZSl9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25LZXkoKGZ1bmN0aW9uKGUpe3JldHVybiBpLl9vbktleShlLmtleSl9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25CbHVyKChmdW5jdGlvbigpe3JldHVybiBpLl9jbGVhckxpdmVSZWdpb24oKX0pKSksaS5yZWdpc3RlcihpLl9yZW5kZXJTZXJ2aWNlLm9uRGltZW5zaW9uc0NoYW5nZSgoZnVuY3Rpb24oKXtyZXR1cm4gaS5fcmVmcmVzaFJvd3NEaW1lbnNpb25zKCl9KSkpLGkuX3NjcmVlbkRwck1vbml0b3I9bmV3IHUuU2NyZWVuRHByTW9uaXRvcixpLnJlZ2lzdGVyKGkuX3NjcmVlbkRwck1vbml0b3IpLGkuX3NjcmVlbkRwck1vbml0b3Iuc2V0TGlzdGVuZXIoKGZ1bmN0aW9uKCl7cmV0dXJuIGkuX3JlZnJlc2hSb3dzRGltZW5zaW9ucygpfSkpLGkucmVnaXN0ZXIoKDAsYy5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHdpbmRvdywicmVzaXplIiwoZnVuY3Rpb24oKXtyZXR1cm4gaS5fcmVmcmVzaFJvd3NEaW1lbnNpb25zKCl9KSkpLGl9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7ZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpLCgwLGgucmVtb3ZlRWxlbWVudEZyb21QYXJlbnQpKHRoaXMuX2FjY2Vzc2liaWxpdHlUcmVlUm9vdCksdGhpcy5fcm93RWxlbWVudHMubGVuZ3RoPTB9LHQucHJvdG90eXBlLl9vbkJvdW5kYXJ5Rm9jdXM9ZnVuY3Rpb24oZSx0KXt2YXIgcj1lLnRhcmdldCxpPXRoaXMuX3Jvd0VsZW1lbnRzWzA9PT10PzE6dGhpcy5fcm93RWxlbWVudHMubGVuZ3RoLTJdO2lmKHIuZ2V0QXR0cmlidXRlKCJhcmlhLXBvc2luc2V0IikhPT0oMD09PXQ/IjEiOiIiK3RoaXMuX3Rlcm1pbmFsLmJ1ZmZlci5saW5lcy5sZW5ndGgpJiZlLnJlbGF0ZWRUYXJnZXQ9PT1pKXt2YXIgbixvO2lmKDA9PT10PyhuPXIsbz10aGlzLl9yb3dFbGVtZW50cy5wb3AoKSx0aGlzLl9yb3dDb250YWluZXIucmVtb3ZlQ2hpbGQobykpOihuPXRoaXMuX3Jvd0VsZW1lbnRzLnNoaWZ0KCksbz1yLHRoaXMuX3Jvd0NvbnRhaW5lci5yZW1vdmVDaGlsZChuKSksbi5yZW1vdmVFdmVudExpc3RlbmVyKCJmb2N1cyIsdGhpcy5fdG9wQm91bmRhcnlGb2N1c0xpc3RlbmVyKSxvLnJlbW92ZUV2ZW50TGlzdGVuZXIoImZvY3VzIix0aGlzLl9ib3R0b21Cb3VuZGFyeUZvY3VzTGlzdGVuZXIpLDA9PT10KXt2YXIgcz10aGlzLl9jcmVhdGVBY2Nlc3NpYmlsaXR5VHJlZU5vZGUoKTt0aGlzLl9yb3dFbGVtZW50cy51bnNoaWZ0KHMpLHRoaXMuX3Jvd0NvbnRhaW5lci5pbnNlcnRBZGphY2VudEVsZW1lbnQoImFmdGVyYmVnaW4iLHMpfWVsc2Ugcz10aGlzLl9jcmVhdGVBY2Nlc3NpYmlsaXR5VHJlZU5vZGUoKSx0aGlzLl9yb3dFbGVtZW50cy5wdXNoKHMpLHRoaXMuX3Jvd0NvbnRhaW5lci5hcHBlbmRDaGlsZChzKTt0aGlzLl9yb3dFbGVtZW50c1swXS5hZGRFdmVudExpc3RlbmVyKCJmb2N1cyIsdGhpcy5fdG9wQm91bmRhcnlGb2N1c0xpc3RlbmVyKSx0aGlzLl9yb3dFbGVtZW50c1t0aGlzLl9yb3dFbGVtZW50cy5sZW5ndGgtMV0uYWRkRXZlbnRMaXN0ZW5lcigiZm9jdXMiLHRoaXMuX2JvdHRvbUJvdW5kYXJ5Rm9jdXNMaXN0ZW5lciksdGhpcy5fdGVybWluYWwuc2Nyb2xsTGluZXMoMD09PXQ/LTE6MSksdGhpcy5fcm93RWxlbWVudHNbMD09PXQ/MTp0aGlzLl9yb3dFbGVtZW50cy5sZW5ndGgtMl0uZm9jdXMoKSxlLnByZXZlbnREZWZhdWx0KCksZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKX19LHQucHJvdG90eXBlLl9vblJlc2l6ZT1mdW5jdGlvbihlKXt0aGlzLl9yb3dFbGVtZW50c1t0aGlzLl9yb3dFbGVtZW50cy5sZW5ndGgtMV0ucmVtb3ZlRXZlbnRMaXN0ZW5lcigiZm9jdXMiLHRoaXMuX2JvdHRvbUJvdW5kYXJ5Rm9jdXNMaXN0ZW5lcik7Zm9yKHZhciB0PXRoaXMuX3Jvd0NvbnRhaW5lci5jaGlsZHJlbi5sZW5ndGg7dDx0aGlzLl90ZXJtaW5hbC5yb3dzO3QrKyl0aGlzLl9yb3dFbGVtZW50c1t0XT10aGlzLl9jcmVhdGVBY2Nlc3NpYmlsaXR5VHJlZU5vZGUoKSx0aGlzLl9yb3dDb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy5fcm93RWxlbWVudHNbdF0pO2Zvcig7dGhpcy5fcm93RWxlbWVudHMubGVuZ3RoPmU7KXRoaXMuX3Jvd0NvbnRhaW5lci5yZW1vdmVDaGlsZCh0aGlzLl9yb3dFbGVtZW50cy5wb3AoKSk7dGhpcy5fcm93RWxlbWVudHNbdGhpcy5fcm93RWxlbWVudHMubGVuZ3RoLTFdLmFkZEV2ZW50TGlzdGVuZXIoImZvY3VzIix0aGlzLl9ib3R0b21Cb3VuZGFyeUZvY3VzTGlzdGVuZXIpLHRoaXMuX3JlZnJlc2hSb3dzRGltZW5zaW9ucygpfSx0LnByb3RvdHlwZS5fY3JlYXRlQWNjZXNzaWJpbGl0eVRyZWVOb2RlPWZ1bmN0aW9uKCl7dmFyIGU9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iik7cmV0dXJuIGUuc2V0QXR0cmlidXRlKCJyb2xlIiwibGlzdGl0ZW0iKSxlLnRhYkluZGV4PS0xLHRoaXMuX3JlZnJlc2hSb3dEaW1lbnNpb25zKGUpLGV9LHQucHJvdG90eXBlLl9vblRhYj1mdW5jdGlvbihlKXtmb3IodmFyIHQ9MDt0PGU7dCsrKXRoaXMuX29uQ2hhcigiICIpfSx0LnByb3RvdHlwZS5fb25DaGFyPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7dGhpcy5fbGl2ZVJlZ2lvbkxpbmVDb3VudDwyMSYmKHRoaXMuX2NoYXJzVG9Db25zdW1lLmxlbmd0aD4wP3RoaXMuX2NoYXJzVG9Db25zdW1lLnNoaWZ0KCkhPT1lJiYodGhpcy5fY2hhcnNUb0Fubm91bmNlKz1lKTp0aGlzLl9jaGFyc1RvQW5ub3VuY2UrPWUsIlxuIj09PWUmJih0aGlzLl9saXZlUmVnaW9uTGluZUNvdW50KyssMjE9PT10aGlzLl9saXZlUmVnaW9uTGluZUNvdW50JiYodGhpcy5fbGl2ZVJlZ2lvbi50ZXh0Q29udGVudCs9by50b29NdWNoT3V0cHV0KSkscy5pc01hYyYmdGhpcy5fbGl2ZVJlZ2lvbi50ZXh0Q29udGVudCYmdGhpcy5fbGl2ZVJlZ2lvbi50ZXh0Q29udGVudC5sZW5ndGg+MCYmIXRoaXMuX2xpdmVSZWdpb24ucGFyZW50Tm9kZSYmc2V0VGltZW91dCgoZnVuY3Rpb24oKXt0Ll9hY2Nlc3NpYmlsaXR5VHJlZVJvb3QuYXBwZW5kQ2hpbGQodC5fbGl2ZVJlZ2lvbil9KSwwKSl9LHQucHJvdG90eXBlLl9jbGVhckxpdmVSZWdpb249ZnVuY3Rpb24oKXt0aGlzLl9saXZlUmVnaW9uLnRleHRDb250ZW50PSIiLHRoaXMuX2xpdmVSZWdpb25MaW5lQ291bnQ9MCxzLmlzTWFjJiYoMCxoLnJlbW92ZUVsZW1lbnRGcm9tUGFyZW50KSh0aGlzLl9saXZlUmVnaW9uKX0sdC5wcm90b3R5cGUuX29uS2V5PWZ1bmN0aW9uKGUpe3RoaXMuX2NsZWFyTGl2ZVJlZ2lvbigpLHRoaXMuX2NoYXJzVG9Db25zdW1lLnB1c2goZSl9LHQucHJvdG90eXBlLl9yZWZyZXNoUm93cz1mdW5jdGlvbihlLHQpe3RoaXMuX3JlbmRlclJvd3NEZWJvdW5jZXIucmVmcmVzaChlLHQsdGhpcy5fdGVybWluYWwucm93cyl9LHQucHJvdG90eXBlLl9yZW5kZXJSb3dzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRoaXMuX3Rlcm1pbmFsLmJ1ZmZlcixpPXIubGluZXMubGVuZ3RoLnRvU3RyaW5nKCksbj1lO248PXQ7bisrKXt2YXIgbz1yLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhyLnlkaXNwK24sITApLHM9KHIueWRpc3ArbisxKS50b1N0cmluZygpLGE9dGhpcy5fcm93RWxlbWVudHNbbl07YSYmKDA9PT1vLmxlbmd0aD9hLmlubmVyVGV4dD0iwqAiOmEudGV4dENvbnRlbnQ9byxhLnNldEF0dHJpYnV0ZSgiYXJpYS1wb3NpbnNldCIscyksYS5zZXRBdHRyaWJ1dGUoImFyaWEtc2V0c2l6ZSIsaSkpfXRoaXMuX2Fubm91bmNlQ2hhcmFjdGVycygpfSx0LnByb3RvdHlwZS5fcmVmcmVzaFJvd3NEaW1lbnNpb25zPWZ1bmN0aW9uKCl7aWYodGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLmFjdHVhbENlbGxIZWlnaHQpe3RoaXMuX3Jvd0VsZW1lbnRzLmxlbmd0aCE9PXRoaXMuX3Rlcm1pbmFsLnJvd3MmJnRoaXMuX29uUmVzaXplKHRoaXMuX3Rlcm1pbmFsLnJvd3MpO2Zvcih2YXIgZT0wO2U8dGhpcy5fdGVybWluYWwucm93cztlKyspdGhpcy5fcmVmcmVzaFJvd0RpbWVuc2lvbnModGhpcy5fcm93RWxlbWVudHNbZV0pfX0sdC5wcm90b3R5cGUuX3JlZnJlc2hSb3dEaW1lbnNpb25zPWZ1bmN0aW9uKGUpe2Uuc3R5bGUuaGVpZ2h0PXRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KyJweCJ9LHQucHJvdG90eXBlLl9hbm5vdW5jZUNoYXJhY3RlcnM9ZnVuY3Rpb24oKXswIT09dGhpcy5fY2hhcnNUb0Fubm91bmNlLmxlbmd0aCYmKHRoaXMuX2xpdmVSZWdpb24udGV4dENvbnRlbnQrPXRoaXMuX2NoYXJzVG9Bbm5vdW5jZSx0aGlzLl9jaGFyc1RvQW5ub3VuY2U9IiIpfSx0fShsLkRpc3Bvc2FibGUpO3QuQWNjZXNzaWJpbGl0eU1hbmFnZXI9Zn0sMzYxNDooZSx0KT0+e2Z1bmN0aW9uIHIoZSl7cmV0dXJuIGUucmVwbGFjZSgvXHI/XG4vZywiXHIiKX1mdW5jdGlvbiBpKGUsdCl7cmV0dXJuIHQ/IhtbMjAwfiIrZSsiG1syMDF+IjplfWZ1bmN0aW9uIG4oZSx0LG4pe2U9aShlPXIoZSksbi5kZWNQcml2YXRlTW9kZXMuYnJhY2tldGVkUGFzdGVNb2RlKSxuLnRyaWdnZXJEYXRhRXZlbnQoZSwhMCksdC52YWx1ZT0iIn1mdW5jdGlvbiBvKGUsdCxyKXt2YXIgaT1yLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLG49ZS5jbGllbnRYLWkubGVmdC0xMCxvPWUuY2xpZW50WS1pLnRvcC0xMDt0LnN0eWxlLndpZHRoPSIyMHB4Iix0LnN0eWxlLmhlaWdodD0iMjBweCIsdC5zdHlsZS5sZWZ0PW4rInB4Iix0LnN0eWxlLnRvcD1vKyJweCIsdC5zdHlsZS56SW5kZXg9IjEwMDAiLHQuZm9jdXMoKX1PYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5yaWdodENsaWNrSGFuZGxlcj10Lm1vdmVUZXh0QXJlYVVuZGVyTW91c2VDdXJzb3I9dC5wYXN0ZT10LmhhbmRsZVBhc3RlRXZlbnQ9dC5jb3B5SGFuZGxlcj10LmJyYWNrZXRUZXh0Rm9yUGFzdGU9dC5wcmVwYXJlVGV4dEZvclRlcm1pbmFsPXZvaWQgMCx0LnByZXBhcmVUZXh0Rm9yVGVybWluYWw9cix0LmJyYWNrZXRUZXh0Rm9yUGFzdGU9aSx0LmNvcHlIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7ZS5jbGlwYm9hcmREYXRhJiZlLmNsaXBib2FyZERhdGEuc2V0RGF0YSgidGV4dC9wbGFpbiIsdC5zZWxlY3Rpb25UZXh0KSxlLnByZXZlbnREZWZhdWx0KCl9LHQuaGFuZGxlUGFzdGVFdmVudD1mdW5jdGlvbihlLHQscil7ZS5zdG9wUHJvcGFnYXRpb24oKSxlLmNsaXBib2FyZERhdGEmJm4oZS5jbGlwYm9hcmREYXRhLmdldERhdGEoInRleHQvcGxhaW4iKSx0LHIpfSx0LnBhc3RlPW4sdC5tb3ZlVGV4dEFyZWFVbmRlck1vdXNlQ3Vyc29yPW8sdC5yaWdodENsaWNrSGFuZGxlcj1mdW5jdGlvbihlLHQscixpLG4pe28oZSx0LHIpLG4mJmkucmlnaHRDbGlja1NlbGVjdChlKSx0LnZhbHVlPWkuc2VsZWN0aW9uVGV4dCx0LnNlbGVjdCgpfX0sNDc3NDooZSx0KT0+e3ZhciByLGksbixvO2Z1bmN0aW9uIHMoZSl7dmFyIHQ9ZS50b1N0cmluZygxNik7cmV0dXJuIHQubGVuZ3RoPDI/IjAiK3Q6dH1mdW5jdGlvbiBhKGUsdCl7cmV0dXJuIGU8dD8odCsuMDUpLyhlKy4wNSk6KGUrLjA1KS8odCsuMDUpfU9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LmNvbnRyYXN0UmF0aW89dC50b1BhZGRlZEhleD10LnJnYmE9dC5yZ2I9dC5jc3M9dC5jb2xvcj10LmNoYW5uZWxzPXZvaWQgMCxmdW5jdGlvbihlKXtlLnRvQ3NzPWZ1bmN0aW9uKGUsdCxyLGkpe3JldHVybiB2b2lkIDAhPT1pPyIjIitzKGUpK3ModCkrcyhyKStzKGkpOiIjIitzKGUpK3ModCkrcyhyKX0sZS50b1JnYmE9ZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuIHZvaWQgMD09PWkmJihpPTI1NSksKGU8PDI0fHQ8PDE2fHI8PDh8aSk+Pj4wfX0ocj10LmNoYW5uZWxzfHwodC5jaGFubmVscz17fSkpLChpPXQuY29sb3J8fCh0LmNvbG9yPXt9KSkuYmxlbmQ9ZnVuY3Rpb24oZSx0KXt2YXIgaT0oMjU1JnQucmdiYSkvMjU1O2lmKDE9PT1pKXJldHVybntjc3M6dC5jc3MscmdiYTp0LnJnYmF9O3ZhciBuPXQucmdiYT4+MjQmMjU1LG89dC5yZ2JhPj4xNiYyNTUscz10LnJnYmE+PjgmMjU1LGE9ZS5yZ2JhPj4yNCYyNTUsYz1lLnJnYmE+PjE2JjI1NSxsPWUucmdiYT4+OCYyNTUsdT1hK01hdGgucm91bmQoKG4tYSkqaSksaD1jK01hdGgucm91bmQoKG8tYykqaSksZj1sK01hdGgucm91bmQoKHMtbCkqaSk7cmV0dXJue2NzczpyLnRvQ3NzKHUsaCxmKSxyZ2JhOnIudG9SZ2JhKHUsaCxmKX19LGkuaXNPcGFxdWU9ZnVuY3Rpb24oZSl7cmV0dXJuIDI1NT09KDI1NSZlLnJnYmEpfSxpLmVuc3VyZUNvbnRyYXN0UmF0aW89ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW8uZW5zdXJlQ29udHJhc3RSYXRpbyhlLnJnYmEsdC5yZ2JhLHIpO2lmKGkpcmV0dXJuIG8udG9Db2xvcihpPj4yNCYyNTUsaT4+MTYmMjU1LGk+PjgmMjU1KX0saS5vcGFxdWU9ZnVuY3Rpb24oZSl7dmFyIHQ9KDI1NXxlLnJnYmEpPj4+MCxpPW8udG9DaGFubmVscyh0KSxuPWlbMF0scz1pWzFdLGE9aVsyXTtyZXR1cm57Y3NzOnIudG9Dc3MobixzLGEpLHJnYmE6dH19LGkub3BhY2l0eT1mdW5jdGlvbihlLHQpe3ZhciBpPU1hdGgucm91bmQoMjU1KnQpLG49by50b0NoYW5uZWxzKGUucmdiYSkscz1uWzBdLGE9blsxXSxjPW5bMl07cmV0dXJue2NzczpyLnRvQ3NzKHMsYSxjLGkpLHJnYmE6ci50b1JnYmEocyxhLGMsaSl9fSxpLnRvQ29sb3JSR0I9ZnVuY3Rpb24oZSl7cmV0dXJuW2UucmdiYT4+MjQmMjU1LGUucmdiYT4+MTYmMjU1LGUucmdiYT4+OCYyNTVdfSwodC5jc3N8fCh0LmNzcz17fSkpLnRvQ29sb3I9ZnVuY3Rpb24oZSl7c3dpdGNoKGUubGVuZ3RoKXtjYXNlIDc6cmV0dXJue2NzczplLHJnYmE6KHBhcnNlSW50KGUuc2xpY2UoMSksMTYpPDw4fDI1NSk+Pj4wfTtjYXNlIDk6cmV0dXJue2NzczplLHJnYmE6cGFyc2VJbnQoZS5zbGljZSgxKSwxNik+Pj4wfX10aHJvdyBuZXcgRXJyb3IoImNzcy50b0NvbG9yOiBVbnN1cHBvcnRlZCBjc3MgZm9ybWF0Iil9LGZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQoZSx0LHIpe3ZhciBpPWUvMjU1LG49dC8yNTUsbz1yLzI1NTtyZXR1cm4uMjEyNiooaTw9LjAzOTI4P2kvMTIuOTI6TWF0aC5wb3coKGkrLjA1NSkvMS4wNTUsMi40KSkrLjcxNTIqKG48PS4wMzkyOD9uLzEyLjkyOk1hdGgucG93KChuKy4wNTUpLzEuMDU1LDIuNCkpKy4wNzIyKihvPD0uMDM5Mjg/by8xMi45MjpNYXRoLnBvdygobysuMDU1KS8xLjA1NSwyLjQpKX1lLnJlbGF0aXZlTHVtaW5hbmNlPWZ1bmN0aW9uKGUpe3JldHVybiB0KGU+PjE2JjI1NSxlPj44JjI1NSwyNTUmZSl9LGUucmVsYXRpdmVMdW1pbmFuY2UyPXR9KG49dC5yZ2J8fCh0LnJnYj17fSkpLGZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQoZSx0LHIpe2Zvcih2YXIgaT1lPj4yNCYyNTUsbz1lPj4xNiYyNTUscz1lPj44JjI1NSxjPXQ+PjI0JjI1NSxsPXQ+PjE2JjI1NSx1PXQ+PjgmMjU1LGg9YShuLnJlbGF0aXZlTHVtaW5hbmNlMihjLHUsbCksbi5yZWxhdGl2ZUx1bWluYW5jZTIoaSxvLHMpKTtoPHImJihjPjB8fGw+MHx8dT4wKTspYy09TWF0aC5tYXgoMCxNYXRoLmNlaWwoLjEqYykpLGwtPU1hdGgubWF4KDAsTWF0aC5jZWlsKC4xKmwpKSx1LT1NYXRoLm1heCgwLE1hdGguY2VpbCguMSp1KSksaD1hKG4ucmVsYXRpdmVMdW1pbmFuY2UyKGMsdSxsKSxuLnJlbGF0aXZlTHVtaW5hbmNlMihpLG8scykpO3JldHVybihjPDwyNHxsPDwxNnx1PDw4fDI1NSk+Pj4wfWZ1bmN0aW9uIGkoZSx0LHIpe2Zvcih2YXIgaT1lPj4yNCYyNTUsbz1lPj4xNiYyNTUscz1lPj44JjI1NSxjPXQ+PjI0JjI1NSxsPXQ+PjE2JjI1NSx1PXQ+PjgmMjU1LGg9YShuLnJlbGF0aXZlTHVtaW5hbmNlMihjLHUsbCksbi5yZWxhdGl2ZUx1bWluYW5jZTIoaSxvLHMpKTtoPHImJihjPDI1NXx8bDwyNTV8fHU8MjU1KTspYz1NYXRoLm1pbigyNTUsYytNYXRoLmNlaWwoLjEqKDI1NS1jKSkpLGw9TWF0aC5taW4oMjU1LGwrTWF0aC5jZWlsKC4xKigyNTUtbCkpKSx1PU1hdGgubWluKDI1NSx1K01hdGguY2VpbCguMSooMjU1LXUpKSksaD1hKG4ucmVsYXRpdmVMdW1pbmFuY2UyKGMsdSxsKSxuLnJlbGF0aXZlTHVtaW5hbmNlMihpLG8scykpO3JldHVybihjPDwyNHxsPDwxNnx1PDw4fDI1NSk+Pj4wfWUuZW5zdXJlQ29udHJhc3RSYXRpbz1mdW5jdGlvbihlLHIsbyl7dmFyIHM9bi5yZWxhdGl2ZUx1bWluYW5jZShlPj44KSxjPW4ucmVsYXRpdmVMdW1pbmFuY2Uocj4+OCk7aWYoYShzLGMpPG8pcmV0dXJuIGM8cz90KGUscixvKTppKGUscixvKX0sZS5yZWR1Y2VMdW1pbmFuY2U9dCxlLmluY3JlYXNlTHVtaW5hbmNlPWksZS50b0NoYW5uZWxzPWZ1bmN0aW9uKGUpe3JldHVybltlPj4yNCYyNTUsZT4+MTYmMjU1LGU+PjgmMjU1LDI1NSZlXX0sZS50b0NvbG9yPWZ1bmN0aW9uKGUsdCxpKXtyZXR1cm57Y3NzOnIudG9Dc3MoZSx0LGkpLHJnYmE6ci50b1JnYmEoZSx0LGkpfX19KG89dC5yZ2JhfHwodC5yZ2JhPXt9KSksdC50b1BhZGRlZEhleD1zLHQuY29udHJhc3RSYXRpbz1hfSw3MjM5OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQ29sb3JDb250cmFzdENhY2hlPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt0aGlzLl9jb2xvcj17fSx0aGlzLl9yZ2JhPXt9fXJldHVybiBlLnByb3RvdHlwZS5jbGVhcj1mdW5jdGlvbigpe3RoaXMuX2NvbG9yPXt9LHRoaXMuX3JnYmE9e319LGUucHJvdG90eXBlLnNldENzcz1mdW5jdGlvbihlLHQscil7dGhpcy5fcmdiYVtlXXx8KHRoaXMuX3JnYmFbZV09e30pLHRoaXMuX3JnYmFbZV1bdF09cn0sZS5wcm90b3R5cGUuZ2V0Q3NzPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMuX3JnYmFbZV0/dGhpcy5fcmdiYVtlXVt0XTp2b2lkIDB9LGUucHJvdG90eXBlLnNldENvbG9yPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9jb2xvcltlXXx8KHRoaXMuX2NvbG9yW2VdPXt9KSx0aGlzLl9jb2xvcltlXVt0XT1yfSxlLnByb3RvdHlwZS5nZXRDb2xvcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9jb2xvcltlXT90aGlzLl9jb2xvcltlXVt0XTp2b2lkIDB9LGV9KCk7dC5Db2xvckNvbnRyYXN0Q2FjaGU9cn0sNTY4MDpmdW5jdGlvbihlLHQscil7dmFyIGk9dGhpcyYmdGhpcy5fX3NwcmVhZEFycmF5fHxmdW5jdGlvbihlLHQscil7aWYocnx8Mj09PWFyZ3VtZW50cy5sZW5ndGgpZm9yKHZhciBpLG49MCxvPXQubGVuZ3RoO248bztuKyspIWkmJm4gaW4gdHx8KGl8fChpPUFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHQsMCxuKSksaVtuXT10W25dKTtyZXR1cm4gZS5jb25jYXQoaXx8QXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwodCkpfTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Db2xvck1hbmFnZXI9dC5ERUZBVUxUX0FOU0lfQ09MT1JTPXZvaWQgMDt2YXIgbj1yKDQ3NzQpLG89cig3MjM5KSxzPW4uY3NzLnRvQ29sb3IoIiNmZmZmZmYiKSxhPW4uY3NzLnRvQ29sb3IoIiMwMDAwMDAiKSxjPW4uY3NzLnRvQ29sb3IoIiNmZmZmZmYiKSxsPW4uY3NzLnRvQ29sb3IoIiMwMDAwMDAiKSx1PXtjc3M6InJnYmEoMjU1LCAyNTUsIDI1NSwgMC4zKSIscmdiYTo0Mjk0OTY3MTE3fTt0LkRFRkFVTFRfQU5TSV9DT0xPUlM9T2JqZWN0LmZyZWV6ZShmdW5jdGlvbigpe2Zvcih2YXIgZT1bbi5jc3MudG9Db2xvcigiIzJlMzQzNiIpLG4uY3NzLnRvQ29sb3IoIiNjYzAwMDAiKSxuLmNzcy50b0NvbG9yKCIjNGU5YTA2Iiksbi5jc3MudG9Db2xvcigiI2M0YTAwMCIpLG4uY3NzLnRvQ29sb3IoIiMzNDY1YTQiKSxuLmNzcy50b0NvbG9yKCIjNzU1MDdiIiksbi5jc3MudG9Db2xvcigiIzA2OTg5YSIpLG4uY3NzLnRvQ29sb3IoIiNkM2Q3Y2YiKSxuLmNzcy50b0NvbG9yKCIjNTU1NzUzIiksbi5jc3MudG9Db2xvcigiI2VmMjkyOSIpLG4uY3NzLnRvQ29sb3IoIiM4YWUyMzQiKSxuLmNzcy50b0NvbG9yKCIjZmNlOTRmIiksbi5jc3MudG9Db2xvcigiIzcyOWZjZiIpLG4uY3NzLnRvQ29sb3IoIiNhZDdmYTgiKSxuLmNzcy50b0NvbG9yKCIjMzRlMmUyIiksbi5jc3MudG9Db2xvcigiI2VlZWVlYyIpXSx0PVswLDk1LDEzNSwxNzUsMjE1LDI1NV0scj0wO3I8MjE2O3IrKyl7dmFyIGk9dFtyLzM2JTZ8MF0sbz10W3IvNiU2fDBdLHM9dFtyJTZdO2UucHVzaCh7Y3NzOm4uY2hhbm5lbHMudG9Dc3MoaSxvLHMpLHJnYmE6bi5jaGFubmVscy50b1JnYmEoaSxvLHMpfSl9Zm9yKHI9MDtyPDI0O3IrKyl7dmFyIGE9OCsxMCpyO2UucHVzaCh7Y3NzOm4uY2hhbm5lbHMudG9Dc3MoYSxhLGEpLHJnYmE6bi5jaGFubmVscy50b1JnYmEoYSxhLGEpfSl9cmV0dXJuIGV9KCkpO3ZhciBoPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHIpe3RoaXMuYWxsb3dUcmFuc3BhcmVuY3k9cjt2YXIgaT1lLmNyZWF0ZUVsZW1lbnQoImNhbnZhcyIpO2kud2lkdGg9MSxpLmhlaWdodD0xO3ZhciBoPWkuZ2V0Q29udGV4dCgiMmQiKTtpZighaCl0aHJvdyBuZXcgRXJyb3IoIkNvdWxkIG5vdCBnZXQgcmVuZGVyaW5nIGNvbnRleHQiKTt0aGlzLl9jdHg9aCx0aGlzLl9jdHguZ2xvYmFsQ29tcG9zaXRlT3BlcmF0aW9uPSJjb3B5Iix0aGlzLl9saXRtdXNDb2xvcj10aGlzLl9jdHguY3JlYXRlTGluZWFyR3JhZGllbnQoMCwwLDEsMSksdGhpcy5fY29udHJhc3RDYWNoZT1uZXcgby5Db2xvckNvbnRyYXN0Q2FjaGUsdGhpcy5jb2xvcnM9e2ZvcmVncm91bmQ6cyxiYWNrZ3JvdW5kOmEsY3Vyc29yOmMsY3Vyc29yQWNjZW50Omwsc2VsZWN0aW9uVHJhbnNwYXJlbnQ6dSxzZWxlY3Rpb25PcGFxdWU6bi5jb2xvci5ibGVuZChhLHUpLGFuc2k6dC5ERUZBVUxUX0FOU0lfQ09MT1JTLnNsaWNlKCksY29udHJhc3RDYWNoZTp0aGlzLl9jb250cmFzdENhY2hlfSx0aGlzLl91cGRhdGVSZXN0b3JlQ29sb3JzKCl9cmV0dXJuIGUucHJvdG90eXBlLm9uT3B0aW9uc0NoYW5nZT1mdW5jdGlvbihlKXsibWluaW11bUNvbnRyYXN0UmF0aW8iPT09ZSYmdGhpcy5fY29udHJhc3RDYWNoZS5jbGVhcigpfSxlLnByb3RvdHlwZS5zZXRUaGVtZT1mdW5jdGlvbihlKXt2b2lkIDA9PT1lJiYoZT17fSksdGhpcy5jb2xvcnMuZm9yZWdyb3VuZD10aGlzLl9wYXJzZUNvbG9yKGUuZm9yZWdyb3VuZCxzKSx0aGlzLmNvbG9ycy5iYWNrZ3JvdW5kPXRoaXMuX3BhcnNlQ29sb3IoZS5iYWNrZ3JvdW5kLGEpLHRoaXMuY29sb3JzLmN1cnNvcj10aGlzLl9wYXJzZUNvbG9yKGUuY3Vyc29yLGMsITApLHRoaXMuY29sb3JzLmN1cnNvckFjY2VudD10aGlzLl9wYXJzZUNvbG9yKGUuY3Vyc29yQWNjZW50LGwsITApLHRoaXMuY29sb3JzLnNlbGVjdGlvblRyYW5zcGFyZW50PXRoaXMuX3BhcnNlQ29sb3IoZS5zZWxlY3Rpb24sdSwhMCksdGhpcy5jb2xvcnMuc2VsZWN0aW9uT3BhcXVlPW4uY29sb3IuYmxlbmQodGhpcy5jb2xvcnMuYmFja2dyb3VuZCx0aGlzLmNvbG9ycy5zZWxlY3Rpb25UcmFuc3BhcmVudCksbi5jb2xvci5pc09wYXF1ZSh0aGlzLmNvbG9ycy5zZWxlY3Rpb25UcmFuc3BhcmVudCkmJih0aGlzLmNvbG9ycy5zZWxlY3Rpb25UcmFuc3BhcmVudD1uLmNvbG9yLm9wYWNpdHkodGhpcy5jb2xvcnMuc2VsZWN0aW9uVHJhbnNwYXJlbnQsLjMpKSx0aGlzLmNvbG9ycy5hbnNpWzBdPXRoaXMuX3BhcnNlQ29sb3IoZS5ibGFjayx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbMF0pLHRoaXMuY29sb3JzLmFuc2lbMV09dGhpcy5fcGFyc2VDb2xvcihlLnJlZCx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbMV0pLHRoaXMuY29sb3JzLmFuc2lbMl09dGhpcy5fcGFyc2VDb2xvcihlLmdyZWVuLHQuREVGQVVMVF9BTlNJX0NPTE9SU1syXSksdGhpcy5jb2xvcnMuYW5zaVszXT10aGlzLl9wYXJzZUNvbG9yKGUueWVsbG93LHQuREVGQVVMVF9BTlNJX0NPTE9SU1szXSksdGhpcy5jb2xvcnMuYW5zaVs0XT10aGlzLl9wYXJzZUNvbG9yKGUuYmx1ZSx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbNF0pLHRoaXMuY29sb3JzLmFuc2lbNV09dGhpcy5fcGFyc2VDb2xvcihlLm1hZ2VudGEsdC5ERUZBVUxUX0FOU0lfQ09MT1JTWzVdKSx0aGlzLmNvbG9ycy5hbnNpWzZdPXRoaXMuX3BhcnNlQ29sb3IoZS5jeWFuLHQuREVGQVVMVF9BTlNJX0NPTE9SU1s2XSksdGhpcy5jb2xvcnMuYW5zaVs3XT10aGlzLl9wYXJzZUNvbG9yKGUud2hpdGUsdC5ERUZBVUxUX0FOU0lfQ09MT1JTWzddKSx0aGlzLmNvbG9ycy5hbnNpWzhdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRCbGFjayx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbOF0pLHRoaXMuY29sb3JzLmFuc2lbOV09dGhpcy5fcGFyc2VDb2xvcihlLmJyaWdodFJlZCx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbOV0pLHRoaXMuY29sb3JzLmFuc2lbMTBdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRHcmVlbix0LkRFRkFVTFRfQU5TSV9DT0xPUlNbMTBdKSx0aGlzLmNvbG9ycy5hbnNpWzExXT10aGlzLl9wYXJzZUNvbG9yKGUuYnJpZ2h0WWVsbG93LHQuREVGQVVMVF9BTlNJX0NPTE9SU1sxMV0pLHRoaXMuY29sb3JzLmFuc2lbMTJdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRCbHVlLHQuREVGQVVMVF9BTlNJX0NPTE9SU1sxMl0pLHRoaXMuY29sb3JzLmFuc2lbMTNdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRNYWdlbnRhLHQuREVGQVVMVF9BTlNJX0NPTE9SU1sxM10pLHRoaXMuY29sb3JzLmFuc2lbMTRdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRDeWFuLHQuREVGQVVMVF9BTlNJX0NPTE9SU1sxNF0pLHRoaXMuY29sb3JzLmFuc2lbMTVdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRXaGl0ZSx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbMTVdKSx0aGlzLl9jb250cmFzdENhY2hlLmNsZWFyKCksdGhpcy5fdXBkYXRlUmVzdG9yZUNvbG9ycygpfSxlLnByb3RvdHlwZS5yZXN0b3JlQ29sb3I9ZnVuY3Rpb24oZSl7aWYodm9pZCAwIT09ZSlzd2l0Y2goZSl7Y2FzZSAyNTY6dGhpcy5jb2xvcnMuZm9yZWdyb3VuZD10aGlzLl9yZXN0b3JlQ29sb3JzLmZvcmVncm91bmQ7YnJlYWs7Y2FzZSAyNTc6dGhpcy5jb2xvcnMuYmFja2dyb3VuZD10aGlzLl9yZXN0b3JlQ29sb3JzLmJhY2tncm91bmQ7YnJlYWs7Y2FzZSAyNTg6dGhpcy5jb2xvcnMuY3Vyc29yPXRoaXMuX3Jlc3RvcmVDb2xvcnMuY3Vyc29yO2JyZWFrO2RlZmF1bHQ6dGhpcy5jb2xvcnMuYW5zaVtlXT10aGlzLl9yZXN0b3JlQ29sb3JzLmFuc2lbZV19ZWxzZSBmb3IodmFyIHQ9MDt0PHRoaXMuX3Jlc3RvcmVDb2xvcnMuYW5zaS5sZW5ndGg7Kyt0KXRoaXMuY29sb3JzLmFuc2lbdF09dGhpcy5fcmVzdG9yZUNvbG9ycy5hbnNpW3RdfSxlLnByb3RvdHlwZS5fdXBkYXRlUmVzdG9yZUNvbG9ycz1mdW5jdGlvbigpe3RoaXMuX3Jlc3RvcmVDb2xvcnM9e2ZvcmVncm91bmQ6dGhpcy5jb2xvcnMuZm9yZWdyb3VuZCxiYWNrZ3JvdW5kOnRoaXMuY29sb3JzLmJhY2tncm91bmQsY3Vyc29yOnRoaXMuY29sb3JzLmN1cnNvcixhbnNpOmkoW10sdGhpcy5jb2xvcnMuYW5zaSwhMCl9fSxlLnByb3RvdHlwZS5fcGFyc2VDb2xvcj1mdW5jdGlvbihlLHQscil7aWYodm9pZCAwPT09ciYmKHI9dGhpcy5hbGxvd1RyYW5zcGFyZW5jeSksdm9pZCAwPT09ZSlyZXR1cm4gdDtpZih0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2xpdG11c0NvbG9yLHRoaXMuX2N0eC5maWxsU3R5bGU9ZSwic3RyaW5nIiE9dHlwZW9mIHRoaXMuX2N0eC5maWxsU3R5bGUpcmV0dXJuIGNvbnNvbGUud2FybigiQ29sb3I6ICIrZSsiIGlzIGludmFsaWQgdXNpbmcgZmFsbGJhY2sgIit0LmNzcyksdDt0aGlzLl9jdHguZmlsbFJlY3QoMCwwLDEsMSk7dmFyIGk9dGhpcy5fY3R4LmdldEltYWdlRGF0YSgwLDAsMSwxKS5kYXRhO2lmKDI1NSE9PWlbM10pe2lmKCFyKXJldHVybiBjb25zb2xlLndhcm4oIkNvbG9yOiAiK2UrIiBpcyB1c2luZyB0cmFuc3BhcmVuY3ksIGJ1dCBhbGxvd1RyYW5zcGFyZW5jeSBpcyBmYWxzZS4gVXNpbmcgZmFsbGJhY2sgIit0LmNzcysiLiIpLHQ7dmFyIG89dGhpcy5fY3R4LmZpbGxTdHlsZS5zdWJzdHJpbmcoNSx0aGlzLl9jdHguZmlsbFN0eWxlLmxlbmd0aC0xKS5zcGxpdCgiLCIpLm1hcCgoZnVuY3Rpb24oZSl7cmV0dXJuIE51bWJlcihlKX0pKSxzPW9bMF0sYT1vWzFdLGM9b1syXSxsPW9bM10sdT1NYXRoLnJvdW5kKDI1NSpsKTtyZXR1cm57cmdiYTpuLmNoYW5uZWxzLnRvUmdiYShzLGEsYyx1KSxjc3M6ZX19cmV0dXJue2Nzczp0aGlzLl9jdHguZmlsbFN0eWxlLHJnYmE6bi5jaGFubmVscy50b1JnYmEoaVswXSxpWzFdLGlbMl0saVszXSl9fSxlfSgpO3QuQ29sb3JNYW5hZ2VyPWh9LDk2MzE6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5yZW1vdmVFbGVtZW50RnJvbVBhcmVudD12b2lkIDAsdC5yZW1vdmVFbGVtZW50RnJvbVBhcmVudD1mdW5jdGlvbigpe2Zvcih2YXIgZSx0PVtdLHI9MDtyPGFyZ3VtZW50cy5sZW5ndGg7cisrKXRbcl09YXJndW1lbnRzW3JdO2Zvcih2YXIgaT0wLG49dDtpPG4ubGVuZ3RoO2krKyl7dmFyIG89bltpXTtudWxsPT09KGU9bnVsbD09bz92b2lkIDA6by5wYXJlbnRFbGVtZW50KXx8dm9pZCAwPT09ZXx8ZS5yZW1vdmVDaGlsZChvKX19fSwzNjU2OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyPXZvaWQgMCx0LmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcj1mdW5jdGlvbihlLHQscixpKXtlLmFkZEV2ZW50TGlzdGVuZXIodCxyLGkpO3ZhciBuPSExO3JldHVybntkaXNwb3NlOmZ1bmN0aW9uKCl7bnx8KG49ITAsZS5yZW1vdmVFdmVudExpc3RlbmVyKHQscixpKSl9fX19LDM1NTE6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Nb3VzZVpvbmU9dC5MaW5raWZpZXI9dm9pZCAwO3ZhciBvPXIoODQ2MCkscz1yKDI1ODUpLGE9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyKXt0aGlzLl9idWZmZXJTZXJ2aWNlPWUsdGhpcy5fbG9nU2VydmljZT10LHRoaXMuX3VuaWNvZGVTZXJ2aWNlPXIsdGhpcy5fbGlua01hdGNoZXJzPVtdLHRoaXMuX25leHRMaW5rTWF0Y2hlcklkPTAsdGhpcy5fb25TaG93TGlua1VuZGVybGluZT1uZXcgby5FdmVudEVtaXR0ZXIsdGhpcy5fb25IaWRlTGlua1VuZGVybGluZT1uZXcgby5FdmVudEVtaXR0ZXIsdGhpcy5fb25MaW5rVG9vbHRpcD1uZXcgby5FdmVudEVtaXR0ZXIsdGhpcy5fcm93c1RvTGlua2lmeT17c3RhcnQ6dm9pZCAwLGVuZDp2b2lkIDB9fXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uU2hvd0xpbmtVbmRlcmxpbmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25TaG93TGlua1VuZGVybGluZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uSGlkZUxpbmtVbmRlcmxpbmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25IaWRlTGlua1VuZGVybGluZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uTGlua1Rvb2x0aXAiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25MaW5rVG9vbHRpcC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5hdHRhY2hUb0RvbT1mdW5jdGlvbihlLHQpe3RoaXMuX2VsZW1lbnQ9ZSx0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyPXR9LGUucHJvdG90eXBlLmxpbmtpZnlSb3dzPWZ1bmN0aW9uKHQscil7dmFyIGk9dGhpczt0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyJiYodm9pZCAwPT09dGhpcy5fcm93c1RvTGlua2lmeS5zdGFydHx8dm9pZCAwPT09dGhpcy5fcm93c1RvTGlua2lmeS5lbmQ/KHRoaXMuX3Jvd3NUb0xpbmtpZnkuc3RhcnQ9dCx0aGlzLl9yb3dzVG9MaW5raWZ5LmVuZD1yKToodGhpcy5fcm93c1RvTGlua2lmeS5zdGFydD1NYXRoLm1pbih0aGlzLl9yb3dzVG9MaW5raWZ5LnN0YXJ0LHQpLHRoaXMuX3Jvd3NUb0xpbmtpZnkuZW5kPU1hdGgubWF4KHRoaXMuX3Jvd3NUb0xpbmtpZnkuZW5kLHIpKSx0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyLmNsZWFyQWxsKHQsciksdGhpcy5fcm93c1RpbWVvdXRJZCYmY2xlYXJUaW1lb3V0KHRoaXMuX3Jvd3NUaW1lb3V0SWQpLHRoaXMuX3Jvd3NUaW1lb3V0SWQ9c2V0VGltZW91dCgoZnVuY3Rpb24oKXtyZXR1cm4gaS5fbGlua2lmeVJvd3MoKX0pLGUuX3RpbWVCZWZvcmVMYXRlbmN5KSl9LGUucHJvdG90eXBlLl9saW5raWZ5Um93cz1mdW5jdGlvbigpe3RoaXMuX3Jvd3NUaW1lb3V0SWQ9dm9pZCAwO3ZhciBlPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyO2lmKHZvaWQgMCE9PXRoaXMuX3Jvd3NUb0xpbmtpZnkuc3RhcnQmJnZvaWQgMCE9PXRoaXMuX3Jvd3NUb0xpbmtpZnkuZW5kKXt2YXIgdD1lLnlkaXNwK3RoaXMuX3Jvd3NUb0xpbmtpZnkuc3RhcnQ7aWYoISh0Pj1lLmxpbmVzLmxlbmd0aCkpe2Zvcih2YXIgcj1lLnlkaXNwK01hdGgubWluKHRoaXMuX3Jvd3NUb0xpbmtpZnkuZW5kLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cykrMSxpPU1hdGguY2VpbCgyZTMvdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSxuPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLml0ZXJhdG9yKCExLHQscixpLGkpO24uaGFzTmV4dCgpOylmb3IodmFyIG89bi5uZXh0KCkscz0wO3M8dGhpcy5fbGlua01hdGNoZXJzLmxlbmd0aDtzKyspdGhpcy5fZG9MaW5raWZ5Um93KG8ucmFuZ2UuZmlyc3Qsby5jb250ZW50LHRoaXMuX2xpbmtNYXRjaGVyc1tzXSk7dGhpcy5fcm93c1RvTGlua2lmeS5zdGFydD12b2lkIDAsdGhpcy5fcm93c1RvTGlua2lmeS5lbmQ9dm9pZCAwfX1lbHNlIHRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIl9yb3dUb0xpbmtpZnkgd2FzIHVuc2V0IGJlZm9yZSBfbGlua2lmeVJvd3Mgd2FzIGNhbGxlZCIpfSxlLnByb3RvdHlwZS5yZWdpc3RlckxpbmtNYXRjaGVyPWZ1bmN0aW9uKGUsdCxyKXtpZih2b2lkIDA9PT1yJiYocj17fSksIXQpdGhyb3cgbmV3IEVycm9yKCJoYW5kbGVyIG11c3QgYmUgZGVmaW5lZCIpO3ZhciBpPXtpZDp0aGlzLl9uZXh0TGlua01hdGNoZXJJZCsrLHJlZ2V4OmUsaGFuZGxlcjp0LG1hdGNoSW5kZXg6ci5tYXRjaEluZGV4LHZhbGlkYXRpb25DYWxsYmFjazpyLnZhbGlkYXRpb25DYWxsYmFjayxob3ZlclRvb2x0aXBDYWxsYmFjazpyLnRvb2x0aXBDYWxsYmFjayxob3ZlckxlYXZlQ2FsbGJhY2s6ci5sZWF2ZUNhbGxiYWNrLHdpbGxMaW5rQWN0aXZhdGU6ci53aWxsTGlua0FjdGl2YXRlLHByaW9yaXR5OnIucHJpb3JpdHl8fDB9O3JldHVybiB0aGlzLl9hZGRMaW5rTWF0Y2hlclRvTGlzdChpKSxpLmlkfSxlLnByb3RvdHlwZS5fYWRkTGlua01hdGNoZXJUb0xpc3Q9ZnVuY3Rpb24oZSl7aWYoMCE9PXRoaXMuX2xpbmtNYXRjaGVycy5sZW5ndGgpe2Zvcih2YXIgdD10aGlzLl9saW5rTWF0Y2hlcnMubGVuZ3RoLTE7dD49MDt0LS0paWYoZS5wcmlvcml0eTw9dGhpcy5fbGlua01hdGNoZXJzW3RdLnByaW9yaXR5KXJldHVybiB2b2lkIHRoaXMuX2xpbmtNYXRjaGVycy5zcGxpY2UodCsxLDAsZSk7dGhpcy5fbGlua01hdGNoZXJzLnNwbGljZSgwLDAsZSl9ZWxzZSB0aGlzLl9saW5rTWF0Y2hlcnMucHVzaChlKX0sZS5wcm90b3R5cGUuZGVyZWdpc3RlckxpbmtNYXRjaGVyPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8dGhpcy5fbGlua01hdGNoZXJzLmxlbmd0aDt0KyspaWYodGhpcy5fbGlua01hdGNoZXJzW3RdLmlkPT09ZSlyZXR1cm4gdGhpcy5fbGlua01hdGNoZXJzLnNwbGljZSh0LDEpLCEwO3JldHVybiExfSxlLnByb3RvdHlwZS5fZG9MaW5raWZ5Um93PWZ1bmN0aW9uKGUsdCxyKXtmb3IodmFyIGksbj10aGlzLG89bmV3IFJlZ0V4cChyLnJlZ2V4LnNvdXJjZSwoci5yZWdleC5mbGFnc3x8IiIpKyJnIikscz0tMSxhPWZ1bmN0aW9uKCl7dmFyIGE9aVsibnVtYmVyIiE9dHlwZW9mIHIubWF0Y2hJbmRleD8wOnIubWF0Y2hJbmRleF07aWYoIWEpcmV0dXJuIGMuX2xvZ1NlcnZpY2UuZGVidWcoIm1hdGNoIGZvdW5kIHdpdGhvdXQgY29ycmVzcG9uZGluZyBtYXRjaEluZGV4IixpLHIpLCJicmVhayI7aWYocz10LmluZGV4T2YoYSxzKzEpLG8ubGFzdEluZGV4PXMrYS5sZW5ndGgsczwwKXJldHVybiJicmVhayI7dmFyIGw9Yy5fYnVmZmVyU2VydmljZS5idWZmZXIuc3RyaW5nSW5kZXhUb0J1ZmZlckluZGV4KGUscyk7aWYobFswXTwwKXJldHVybiJicmVhayI7dmFyIHU9Yy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KGxbMF0pO2lmKCF1KXJldHVybiJicmVhayI7dmFyIGg9dS5nZXRGZyhsWzFdKSxmPWg/aD4+OSY1MTE6dm9pZCAwO3IudmFsaWRhdGlvbkNhbGxiYWNrP3IudmFsaWRhdGlvbkNhbGxiYWNrKGEsKGZ1bmN0aW9uKGUpe24uX3Jvd3NUaW1lb3V0SWR8fGUmJm4uX2FkZExpbmsobFsxXSxsWzBdLW4uX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwLGEscixmKX0pKTpjLl9hZGRMaW5rKGxbMV0sbFswXS1jLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCxhLHIsZil9LGM9dGhpcztudWxsIT09KGk9by5leGVjKHQpKSYmImJyZWFrIiE9PWEoKTspO30sZS5wcm90b3R5cGUuX2FkZExpbms9ZnVuY3Rpb24oZSx0LHIsaSxuKXt2YXIgbz10aGlzO2lmKHRoaXMuX21vdXNlWm9uZU1hbmFnZXImJnRoaXMuX2VsZW1lbnQpe3ZhciBzPXRoaXMuX3VuaWNvZGVTZXJ2aWNlLmdldFN0cmluZ0NlbGxXaWR0aChyKSxhPWUldGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGw9dCtNYXRoLmZsb29yKGUvdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSx1PShhK3MpJXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyxoPWwrTWF0aC5mbG9vcigoYStzKS90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpOzA9PT11JiYodT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsaC0tKSx0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyLmFkZChuZXcgYyhhKzEsbCsxLHUrMSxoKzEsKGZ1bmN0aW9uKGUpe2lmKGkuaGFuZGxlcilyZXR1cm4gaS5oYW5kbGVyKGUscik7dmFyIHQ9d2luZG93Lm9wZW4oKTt0Pyh0Lm9wZW5lcj1udWxsLHQubG9jYXRpb24uaHJlZj1yKTpjb25zb2xlLndhcm4oIk9wZW5pbmcgbGluayBibG9ja2VkIGFzIG9wZW5lciBjb3VsZCBub3QgYmUgY2xlYXJlZCIpfSksKGZ1bmN0aW9uKCl7by5fb25TaG93TGlua1VuZGVybGluZS5maXJlKG8uX2NyZWF0ZUxpbmtIb3ZlckV2ZW50KGEsbCx1LGgsbikpLG8uX2VsZW1lbnQuY2xhc3NMaXN0LmFkZCgieHRlcm0tY3Vyc29yLXBvaW50ZXIiKX0pLChmdW5jdGlvbihlKXtvLl9vbkxpbmtUb29sdGlwLmZpcmUoby5fY3JlYXRlTGlua0hvdmVyRXZlbnQoYSxsLHUsaCxuKSksaS5ob3ZlclRvb2x0aXBDYWxsYmFjayYmaS5ob3ZlclRvb2x0aXBDYWxsYmFjayhlLHIse3N0YXJ0Ont4OmEseTpsfSxlbmQ6e3g6dSx5Omh9fSl9KSwoZnVuY3Rpb24oKXtvLl9vbkhpZGVMaW5rVW5kZXJsaW5lLmZpcmUoby5fY3JlYXRlTGlua0hvdmVyRXZlbnQoYSxsLHUsaCxuKSksby5fZWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCJ4dGVybS1jdXJzb3ItcG9pbnRlciIpLGkuaG92ZXJMZWF2ZUNhbGxiYWNrJiZpLmhvdmVyTGVhdmVDYWxsYmFjaygpfSksKGZ1bmN0aW9uKGUpe3JldHVybiFpLndpbGxMaW5rQWN0aXZhdGV8fGkud2lsbExpbmtBY3RpdmF0ZShlLHIpfSkpKX19LGUucHJvdG90eXBlLl9jcmVhdGVMaW5rSG92ZXJFdmVudD1mdW5jdGlvbihlLHQscixpLG4pe3JldHVybnt4MTplLHkxOnQseDI6cix5MjppLGNvbHM6dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGZnOm59fSxlLl90aW1lQmVmb3JlTGF0ZW5jeT0yMDAsZT1pKFtuKDAscy5JQnVmZmVyU2VydmljZSksbigxLHMuSUxvZ1NlcnZpY2UpLG4oMixzLklVbmljb2RlU2VydmljZSldLGUpfSgpO3QuTGlua2lmaWVyPWE7dmFyIGM9ZnVuY3Rpb24oZSx0LHIsaSxuLG8scyxhLGMpe3RoaXMueDE9ZSx0aGlzLnkxPXQsdGhpcy54Mj1yLHRoaXMueTI9aSx0aGlzLmNsaWNrQ2FsbGJhY2s9bix0aGlzLmhvdmVyQ2FsbGJhY2s9byx0aGlzLnRvb2x0aXBDYWxsYmFjaz1zLHRoaXMubGVhdmVDYWxsYmFjaz1hLHRoaXMud2lsbExpbmtBY3RpdmF0ZT1jfTt0Lk1vdXNlWm9uZT1jfSw2NDY1OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkxpbmtpZmllcjI9dm9pZCAwO3ZhciBhPXIoMjU4NSksYz1yKDg0NjApLGw9cig4NDQpLHU9cigzNjU2KSxoPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCl7dmFyIHI9ZS5jYWxsKHRoaXMpfHx0aGlzO3JldHVybiByLl9idWZmZXJTZXJ2aWNlPXQsci5fbGlua1Byb3ZpZGVycz1bXSxyLl9saW5rQ2FjaGVEaXNwb3NhYmxlcz1bXSxyLl9pc01vdXNlT3V0PSEwLHIuX2FjdGl2ZUxpbmU9LTEsci5fb25TaG93TGlua1VuZGVybGluZT1yLnJlZ2lzdGVyKG5ldyBjLkV2ZW50RW1pdHRlciksci5fb25IaWRlTGlua1VuZGVybGluZT1yLnJlZ2lzdGVyKG5ldyBjLkV2ZW50RW1pdHRlciksci5yZWdpc3RlcigoMCxsLmdldERpc3Bvc2VBcnJheURpc3Bvc2FibGUpKHIuX2xpbmtDYWNoZURpc3Bvc2FibGVzKSkscn1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwiY3VycmVudExpbmsiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY3VycmVudExpbmt9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblNob3dMaW5rVW5kZXJsaW5lIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uU2hvd0xpbmtVbmRlcmxpbmUuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkhpZGVMaW5rVW5kZXJsaW5lIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uSGlkZUxpbmtVbmRlcmxpbmUuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUucmVnaXN0ZXJMaW5rUHJvdmlkZXI9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcztyZXR1cm4gdGhpcy5fbGlua1Byb3ZpZGVycy5wdXNoKGUpLHtkaXNwb3NlOmZ1bmN0aW9uKCl7dmFyIHI9dC5fbGlua1Byb3ZpZGVycy5pbmRleE9mKGUpOy0xIT09ciYmdC5fbGlua1Byb3ZpZGVycy5zcGxpY2UociwxKX19fSx0LnByb3RvdHlwZS5hdHRhY2hUb0RvbT1mdW5jdGlvbihlLHQscil7dmFyIGk9dGhpczt0aGlzLl9lbGVtZW50PWUsdGhpcy5fbW91c2VTZXJ2aWNlPXQsdGhpcy5fcmVuZGVyU2VydmljZT1yLHRoaXMucmVnaXN0ZXIoKDAsdS5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMuX2VsZW1lbnQsIm1vdXNlbGVhdmUiLChmdW5jdGlvbigpe2kuX2lzTW91c2VPdXQ9ITAsaS5fY2xlYXJDdXJyZW50TGluaygpfSkpKSx0aGlzLnJlZ2lzdGVyKCgwLHUuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLl9lbGVtZW50LCJtb3VzZW1vdmUiLHRoaXMuX29uTW91c2VNb3ZlLmJpbmQodGhpcykpKSx0aGlzLnJlZ2lzdGVyKCgwLHUuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLl9lbGVtZW50LCJjbGljayIsdGhpcy5fb25DbGljay5iaW5kKHRoaXMpKSl9LHQucHJvdG90eXBlLl9vbk1vdXNlTW92ZT1mdW5jdGlvbihlKXtpZih0aGlzLl9sYXN0TW91c2VFdmVudD1lLHRoaXMuX2VsZW1lbnQmJnRoaXMuX21vdXNlU2VydmljZSl7dmFyIHQ9dGhpcy5fcG9zaXRpb25Gcm9tTW91c2VFdmVudChlLHRoaXMuX2VsZW1lbnQsdGhpcy5fbW91c2VTZXJ2aWNlKTtpZih0KXt0aGlzLl9pc01vdXNlT3V0PSExO2Zvcih2YXIgcj1lLmNvbXBvc2VkUGF0aCgpLGk9MDtpPHIubGVuZ3RoO2krKyl7dmFyIG49cltpXTtpZihuLmNsYXNzTGlzdC5jb250YWlucygieHRlcm0iKSlicmVhaztpZihuLmNsYXNzTGlzdC5jb250YWlucygieHRlcm0taG92ZXIiKSlyZXR1cm59dGhpcy5fbGFzdEJ1ZmZlckNlbGwmJnQueD09PXRoaXMuX2xhc3RCdWZmZXJDZWxsLngmJnQueT09PXRoaXMuX2xhc3RCdWZmZXJDZWxsLnl8fCh0aGlzLl9vbkhvdmVyKHQpLHRoaXMuX2xhc3RCdWZmZXJDZWxsPXQpfX19LHQucHJvdG90eXBlLl9vbkhvdmVyPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUxpbmUhPT1lLnkpcmV0dXJuIHRoaXMuX2NsZWFyQ3VycmVudExpbmsoKSx2b2lkIHRoaXMuX2Fza0ZvckxpbmsoZSwhMSk7dGhpcy5fY3VycmVudExpbmsmJnRoaXMuX2xpbmtBdFBvc2l0aW9uKHRoaXMuX2N1cnJlbnRMaW5rLmxpbmssZSl8fCh0aGlzLl9jbGVhckN1cnJlbnRMaW5rKCksdGhpcy5fYXNrRm9yTGluayhlLCEwKSl9LHQucHJvdG90eXBlLl9hc2tGb3JMaW5rPWZ1bmN0aW9uKGUsdCl7dmFyIHIsaT10aGlzO3RoaXMuX2FjdGl2ZVByb3ZpZGVyUmVwbGllcyYmdHx8KG51bGw9PT0ocj10aGlzLl9hY3RpdmVQcm92aWRlclJlcGxpZXMpfHx2b2lkIDA9PT1yfHxyLmZvckVhY2goKGZ1bmN0aW9uKGUpe251bGw9PWV8fGUuZm9yRWFjaCgoZnVuY3Rpb24oZSl7ZS5saW5rLmRpc3Bvc2UmJmUubGluay5kaXNwb3NlKCl9KSl9KSksdGhpcy5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzPW5ldyBNYXAsdGhpcy5fYWN0aXZlTGluZT1lLnkpO3ZhciBuPSExO3RoaXMuX2xpbmtQcm92aWRlcnMuZm9yRWFjaCgoZnVuY3Rpb24ocixvKXt2YXIgczt0PyhudWxsPT09KHM9aS5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzKXx8dm9pZCAwPT09cz92b2lkIDA6cy5nZXQobykpJiYobj1pLl9jaGVja0xpbmtQcm92aWRlclJlc3VsdChvLGUsbikpOnIucHJvdmlkZUxpbmtzKGUueSwoZnVuY3Rpb24odCl7dmFyIHIscztpZighaS5faXNNb3VzZU91dCl7dmFyIGE9bnVsbD09dD92b2lkIDA6dC5tYXAoKGZ1bmN0aW9uKGUpe3JldHVybntsaW5rOmV9fSkpO251bGw9PT0ocj1pLl9hY3RpdmVQcm92aWRlclJlcGxpZXMpfHx2b2lkIDA9PT1yfHxyLnNldChvLGEpLG49aS5fY2hlY2tMaW5rUHJvdmlkZXJSZXN1bHQobyxlLG4pLChudWxsPT09KHM9aS5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzKXx8dm9pZCAwPT09cz92b2lkIDA6cy5zaXplKT09PWkuX2xpbmtQcm92aWRlcnMubGVuZ3RoJiZpLl9yZW1vdmVJbnRlcnNlY3RpbmdMaW5rcyhlLnksaS5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzKX19KSl9KSl9LHQucHJvdG90eXBlLl9yZW1vdmVJbnRlcnNlY3RpbmdMaW5rcz1mdW5jdGlvbihlLHQpe2Zvcih2YXIgcj1uZXcgU2V0LGk9MDtpPHQuc2l6ZTtpKyspe3ZhciBuPXQuZ2V0KGkpO2lmKG4pZm9yKHZhciBvPTA7bzxuLmxlbmd0aDtvKyspZm9yKHZhciBzPW5bb10sYT1zLmxpbmsucmFuZ2Uuc3RhcnQueTxlPzA6cy5saW5rLnJhbmdlLnN0YXJ0LngsYz1zLmxpbmsucmFuZ2UuZW5kLnk+ZT90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM6cy5saW5rLnJhbmdlLmVuZC54LGw9YTtsPD1jO2wrKyl7aWYoci5oYXMobCkpe24uc3BsaWNlKG8tLSwxKTticmVha31yLmFkZChsKX19fSx0LnByb3RvdHlwZS5fY2hlY2tMaW5rUHJvdmlkZXJSZXN1bHQ9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcztpZighdGhpcy5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzKXJldHVybiByO2Zvcih2YXIgbz10aGlzLl9hY3RpdmVQcm92aWRlclJlcGxpZXMuZ2V0KGUpLHM9ITEsYT0wO2E8ZTthKyspdGhpcy5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzLmhhcyhhKSYmIXRoaXMuX2FjdGl2ZVByb3ZpZGVyUmVwbGllcy5nZXQoYSl8fChzPSEwKTtpZighcyYmbyl7dmFyIGM9by5maW5kKChmdW5jdGlvbihlKXtyZXR1cm4gbi5fbGlua0F0UG9zaXRpb24oZS5saW5rLHQpfSkpO2MmJihyPSEwLHRoaXMuX2hhbmRsZU5ld0xpbmsoYykpfWlmKHRoaXMuX2FjdGl2ZVByb3ZpZGVyUmVwbGllcy5zaXplPT09dGhpcy5fbGlua1Byb3ZpZGVycy5sZW5ndGgmJiFyKWZvcihhPTA7YTx0aGlzLl9hY3RpdmVQcm92aWRlclJlcGxpZXMuc2l6ZTthKyspe3ZhciBsPW51bGw9PT0oaT10aGlzLl9hY3RpdmVQcm92aWRlclJlcGxpZXMuZ2V0KGEpKXx8dm9pZCAwPT09aT92b2lkIDA6aS5maW5kKChmdW5jdGlvbihlKXtyZXR1cm4gbi5fbGlua0F0UG9zaXRpb24oZS5saW5rLHQpfSkpO2lmKGwpe3I9ITAsdGhpcy5faGFuZGxlTmV3TGluayhsKTticmVha319cmV0dXJuIHJ9LHQucHJvdG90eXBlLl9vbkNsaWNrPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2VsZW1lbnQmJnRoaXMuX21vdXNlU2VydmljZSYmdGhpcy5fY3VycmVudExpbmspe3ZhciB0PXRoaXMuX3Bvc2l0aW9uRnJvbU1vdXNlRXZlbnQoZSx0aGlzLl9lbGVtZW50LHRoaXMuX21vdXNlU2VydmljZSk7dCYmdGhpcy5fbGlua0F0UG9zaXRpb24odGhpcy5fY3VycmVudExpbmsubGluayx0KSYmdGhpcy5fY3VycmVudExpbmsubGluay5hY3RpdmF0ZShlLHRoaXMuX2N1cnJlbnRMaW5rLmxpbmsudGV4dCl9fSx0LnByb3RvdHlwZS5fY2xlYXJDdXJyZW50TGluaz1mdW5jdGlvbihlLHQpe3RoaXMuX2VsZW1lbnQmJnRoaXMuX2N1cnJlbnRMaW5rJiZ0aGlzLl9sYXN0TW91c2VFdmVudCYmKCFlfHwhdHx8dGhpcy5fY3VycmVudExpbmsubGluay5yYW5nZS5zdGFydC55Pj1lJiZ0aGlzLl9jdXJyZW50TGluay5saW5rLnJhbmdlLmVuZC55PD10KSYmKHRoaXMuX2xpbmtMZWF2ZSh0aGlzLl9lbGVtZW50LHRoaXMuX2N1cnJlbnRMaW5rLmxpbmssdGhpcy5fbGFzdE1vdXNlRXZlbnQpLHRoaXMuX2N1cnJlbnRMaW5rPXZvaWQgMCwoMCxsLmRpc3Bvc2VBcnJheSkodGhpcy5fbGlua0NhY2hlRGlzcG9zYWJsZXMpKX0sdC5wcm90b3R5cGUuX2hhbmRsZU5ld0xpbms9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcztpZih0aGlzLl9lbGVtZW50JiZ0aGlzLl9sYXN0TW91c2VFdmVudCYmdGhpcy5fbW91c2VTZXJ2aWNlKXt2YXIgcj10aGlzLl9wb3NpdGlvbkZyb21Nb3VzZUV2ZW50KHRoaXMuX2xhc3RNb3VzZUV2ZW50LHRoaXMuX2VsZW1lbnQsdGhpcy5fbW91c2VTZXJ2aWNlKTtyJiZ0aGlzLl9saW5rQXRQb3NpdGlvbihlLmxpbmsscikmJih0aGlzLl9jdXJyZW50TGluaz1lLHRoaXMuX2N1cnJlbnRMaW5rLnN0YXRlPXtkZWNvcmF0aW9uczp7dW5kZXJsaW5lOnZvaWQgMD09PWUubGluay5kZWNvcmF0aW9uc3x8ZS5saW5rLmRlY29yYXRpb25zLnVuZGVybGluZSxwb2ludGVyQ3Vyc29yOnZvaWQgMD09PWUubGluay5kZWNvcmF0aW9uc3x8ZS5saW5rLmRlY29yYXRpb25zLnBvaW50ZXJDdXJzb3J9LGlzSG92ZXJlZDohMH0sdGhpcy5fbGlua0hvdmVyKHRoaXMuX2VsZW1lbnQsZS5saW5rLHRoaXMuX2xhc3RNb3VzZUV2ZW50KSxlLmxpbmsuZGVjb3JhdGlvbnM9e30sT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoZS5saW5rLmRlY29yYXRpb25zLHtwb2ludGVyQ3Vyc29yOntnZXQ6ZnVuY3Rpb24oKXt2YXIgZSxyO3JldHVybiBudWxsPT09KHI9bnVsbD09PShlPXQuX2N1cnJlbnRMaW5rKXx8dm9pZCAwPT09ZT92b2lkIDA6ZS5zdGF0ZSl8fHZvaWQgMD09PXI/dm9pZCAwOnIuZGVjb3JhdGlvbnMucG9pbnRlckN1cnNvcn0sc2V0OmZ1bmN0aW9uKGUpe3ZhciByLGk7KG51bGw9PT0ocj10Ll9jdXJyZW50TGluayl8fHZvaWQgMD09PXI/dm9pZCAwOnIuc3RhdGUpJiZ0Ll9jdXJyZW50TGluay5zdGF0ZS5kZWNvcmF0aW9ucy5wb2ludGVyQ3Vyc29yIT09ZSYmKHQuX2N1cnJlbnRMaW5rLnN0YXRlLmRlY29yYXRpb25zLnBvaW50ZXJDdXJzb3I9ZSx0Ll9jdXJyZW50TGluay5zdGF0ZS5pc0hvdmVyZWQmJihudWxsPT09KGk9dC5fZWxlbWVudCl8fHZvaWQgMD09PWl8fGkuY2xhc3NMaXN0LnRvZ2dsZSgieHRlcm0tY3Vyc29yLXBvaW50ZXIiLGUpKSl9fSx1bmRlcmxpbmU6e2dldDpmdW5jdGlvbigpe3ZhciBlLHI7cmV0dXJuIG51bGw9PT0ocj1udWxsPT09KGU9dC5fY3VycmVudExpbmspfHx2b2lkIDA9PT1lP3ZvaWQgMDplLnN0YXRlKXx8dm9pZCAwPT09cj92b2lkIDA6ci5kZWNvcmF0aW9ucy51bmRlcmxpbmV9LHNldDpmdW5jdGlvbihyKXt2YXIgaSxuLG87KG51bGw9PT0oaT10Ll9jdXJyZW50TGluayl8fHZvaWQgMD09PWk/dm9pZCAwOmkuc3RhdGUpJiYobnVsbD09PShvPW51bGw9PT0obj10Ll9jdXJyZW50TGluayl8fHZvaWQgMD09PW4/dm9pZCAwOm4uc3RhdGUpfHx2b2lkIDA9PT1vP3ZvaWQgMDpvLmRlY29yYXRpb25zLnVuZGVybGluZSkhPT1yJiYodC5fY3VycmVudExpbmsuc3RhdGUuZGVjb3JhdGlvbnMudW5kZXJsaW5lPXIsdC5fY3VycmVudExpbmsuc3RhdGUuaXNIb3ZlcmVkJiZ0Ll9maXJlVW5kZXJsaW5lRXZlbnQoZS5saW5rLHIpKX19fSksdGhpcy5fcmVuZGVyU2VydmljZSYmdGhpcy5fbGlua0NhY2hlRGlzcG9zYWJsZXMucHVzaCh0aGlzLl9yZW5kZXJTZXJ2aWNlLm9uUmVuZGVyZWRCdWZmZXJDaGFuZ2UoKGZ1bmN0aW9uKGUpe3ZhciByPTA9PT1lLnN0YXJ0PzA6ZS5zdGFydCsxK3QuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwO3QuX2NsZWFyQ3VycmVudExpbmsocixlLmVuZCsxK3QuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwKX0pKSkpfX0sdC5wcm90b3R5cGUuX2xpbmtIb3Zlcj1mdW5jdGlvbihlLHQscil7dmFyIGk7KG51bGw9PT0oaT10aGlzLl9jdXJyZW50TGluayl8fHZvaWQgMD09PWk/dm9pZCAwOmkuc3RhdGUpJiYodGhpcy5fY3VycmVudExpbmsuc3RhdGUuaXNIb3ZlcmVkPSEwLHRoaXMuX2N1cnJlbnRMaW5rLnN0YXRlLmRlY29yYXRpb25zLnVuZGVybGluZSYmdGhpcy5fZmlyZVVuZGVybGluZUV2ZW50KHQsITApLHRoaXMuX2N1cnJlbnRMaW5rLnN0YXRlLmRlY29yYXRpb25zLnBvaW50ZXJDdXJzb3ImJmUuY2xhc3NMaXN0LmFkZCgieHRlcm0tY3Vyc29yLXBvaW50ZXIiKSksdC5ob3ZlciYmdC5ob3ZlcihyLHQudGV4dCl9LHQucHJvdG90eXBlLl9maXJlVW5kZXJsaW5lRXZlbnQ9ZnVuY3Rpb24oZSx0KXt2YXIgcj1lLnJhbmdlLGk9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3Asbj10aGlzLl9jcmVhdGVMaW5rVW5kZXJsaW5lRXZlbnQoci5zdGFydC54LTEsci5zdGFydC55LWktMSxyLmVuZC54LHIuZW5kLnktaS0xLHZvaWQgMCk7KHQ/dGhpcy5fb25TaG93TGlua1VuZGVybGluZTp0aGlzLl9vbkhpZGVMaW5rVW5kZXJsaW5lKS5maXJlKG4pfSx0LnByb3RvdHlwZS5fbGlua0xlYXZlPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaTsobnVsbD09PShpPXRoaXMuX2N1cnJlbnRMaW5rKXx8dm9pZCAwPT09aT92b2lkIDA6aS5zdGF0ZSkmJih0aGlzLl9jdXJyZW50TGluay5zdGF0ZS5pc0hvdmVyZWQ9ITEsdGhpcy5fY3VycmVudExpbmsuc3RhdGUuZGVjb3JhdGlvbnMudW5kZXJsaW5lJiZ0aGlzLl9maXJlVW5kZXJsaW5lRXZlbnQodCwhMSksdGhpcy5fY3VycmVudExpbmsuc3RhdGUuZGVjb3JhdGlvbnMucG9pbnRlckN1cnNvciYmZS5jbGFzc0xpc3QucmVtb3ZlKCJ4dGVybS1jdXJzb3ItcG9pbnRlciIpKSx0LmxlYXZlJiZ0LmxlYXZlKHIsdC50ZXh0KX0sdC5wcm90b3R5cGUuX2xpbmtBdFBvc2l0aW9uPWZ1bmN0aW9uKGUsdCl7dmFyIHI9ZS5yYW5nZS5zdGFydC55PT09ZS5yYW5nZS5lbmQueSxpPWUucmFuZ2Uuc3RhcnQueTx0Lnksbj1lLnJhbmdlLmVuZC55PnQueTtyZXR1cm4ociYmZS5yYW5nZS5zdGFydC54PD10LngmJmUucmFuZ2UuZW5kLng+PXQueHx8aSYmZS5yYW5nZS5lbmQueD49dC54fHxuJiZlLnJhbmdlLnN0YXJ0Lng8PXQueHx8aSYmbikmJmUucmFuZ2Uuc3RhcnQueTw9dC55JiZlLnJhbmdlLmVuZC55Pj10Lnl9LHQucHJvdG90eXBlLl9wb3NpdGlvbkZyb21Nb3VzZUV2ZW50PWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1yLmdldENvb3JkcyhlLHQsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyk7aWYoaSlyZXR1cm57eDppWzBdLHk6aVsxXSt0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcH19LHQucHJvdG90eXBlLl9jcmVhdGVMaW5rVW5kZXJsaW5lRXZlbnQ9ZnVuY3Rpb24oZSx0LHIsaSxuKXtyZXR1cm57eDE6ZSx5MTp0LHgyOnIseTI6aSxjb2xzOnRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyxmZzpufX0sbyhbcygwLGEuSUJ1ZmZlclNlcnZpY2UpXSx0KX0obC5EaXNwb3NhYmxlKTt0LkxpbmtpZmllcjI9aH0sOTA0MjooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LnRvb011Y2hPdXRwdXQ9dC5wcm9tcHRMYWJlbD12b2lkIDAsdC5wcm9tcHRMYWJlbD0iVGVybWluYWwgaW5wdXQiLHQudG9vTXVjaE91dHB1dD0iVG9vIG11Y2ggb3V0cHV0IHRvIGFubm91bmNlLCBuYXZpZ2F0ZSB0byByb3dzIG1hbnVhbGx5IHRvIHJlYWQifSw2OTU0OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0Lk1vdXNlWm9uZU1hbmFnZXI9dm9pZCAwO3ZhciBhPXIoODQ0KSxjPXIoMzY1NiksbD1yKDQ3MjUpLHU9cigyNTg1KSxoPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyLGksbixvLHMpe3ZhciBhPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gYS5fZWxlbWVudD10LGEuX3NjcmVlbkVsZW1lbnQ9cixhLl9idWZmZXJTZXJ2aWNlPWksYS5fbW91c2VTZXJ2aWNlPW4sYS5fc2VsZWN0aW9uU2VydmljZT1vLGEuX29wdGlvbnNTZXJ2aWNlPXMsYS5fem9uZXM9W10sYS5fYXJlWm9uZXNBY3RpdmU9ITEsYS5fbGFzdEhvdmVyQ29vcmRzPVt2b2lkIDAsdm9pZCAwXSxhLl9pbml0aWFsU2VsZWN0aW9uTGVuZ3RoPTAsYS5yZWdpc3RlcigoMCxjLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikoYS5fZWxlbWVudCwibW91c2Vkb3duIiwoZnVuY3Rpb24oZSl7cmV0dXJuIGEuX29uTW91c2VEb3duKGUpfSkpKSxhLl9tb3VzZU1vdmVMaXN0ZW5lcj1mdW5jdGlvbihlKXtyZXR1cm4gYS5fb25Nb3VzZU1vdmUoZSl9LGEuX21vdXNlTGVhdmVMaXN0ZW5lcj1mdW5jdGlvbihlKXtyZXR1cm4gYS5fb25Nb3VzZUxlYXZlKGUpfSxhLl9jbGlja0xpc3RlbmVyPWZ1bmN0aW9uKGUpe3JldHVybiBhLl9vbkNsaWNrKGUpfSxhfXJldHVybiBuKHQsZSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe2UucHJvdG90eXBlLmRpc3Bvc2UuY2FsbCh0aGlzKSx0aGlzLl9kZWFjdGl2YXRlKCl9LHQucHJvdG90eXBlLmFkZD1mdW5jdGlvbihlKXt0aGlzLl96b25lcy5wdXNoKGUpLDE9PT10aGlzLl96b25lcy5sZW5ndGgmJnRoaXMuX2FjdGl2YXRlKCl9LHQucHJvdG90eXBlLmNsZWFyQWxsPWZ1bmN0aW9uKGUsdCl7aWYoMCE9PXRoaXMuX3pvbmVzLmxlbmd0aCl7ZSYmdHx8KGU9MCx0PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xKTtmb3IodmFyIHI9MDtyPHRoaXMuX3pvbmVzLmxlbmd0aDtyKyspe3ZhciBpPXRoaXMuX3pvbmVzW3JdOyhpLnkxPmUmJmkueTE8PXQrMXx8aS55Mj5lJiZpLnkyPD10KzF8fGkueTE8ZSYmaS55Mj50KzEpJiYodGhpcy5fY3VycmVudFpvbmUmJnRoaXMuX2N1cnJlbnRab25lPT09aSYmKHRoaXMuX2N1cnJlbnRab25lLmxlYXZlQ2FsbGJhY2soKSx0aGlzLl9jdXJyZW50Wm9uZT12b2lkIDApLHRoaXMuX3pvbmVzLnNwbGljZShyLS0sMSkpfTA9PT10aGlzLl96b25lcy5sZW5ndGgmJnRoaXMuX2RlYWN0aXZhdGUoKX19LHQucHJvdG90eXBlLl9hY3RpdmF0ZT1mdW5jdGlvbigpe3RoaXMuX2FyZVpvbmVzQWN0aXZlfHwodGhpcy5fYXJlWm9uZXNBY3RpdmU9ITAsdGhpcy5fZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCJtb3VzZW1vdmUiLHRoaXMuX21vdXNlTW92ZUxpc3RlbmVyKSx0aGlzLl9lbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNlbGVhdmUiLHRoaXMuX21vdXNlTGVhdmVMaXN0ZW5lciksdGhpcy5fZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsdGhpcy5fY2xpY2tMaXN0ZW5lcikpfSx0LnByb3RvdHlwZS5fZGVhY3RpdmF0ZT1mdW5jdGlvbigpe3RoaXMuX2FyZVpvbmVzQWN0aXZlJiYodGhpcy5fYXJlWm9uZXNBY3RpdmU9ITEsdGhpcy5fZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCJtb3VzZW1vdmUiLHRoaXMuX21vdXNlTW92ZUxpc3RlbmVyKSx0aGlzLl9lbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoIm1vdXNlbGVhdmUiLHRoaXMuX21vdXNlTGVhdmVMaXN0ZW5lciksdGhpcy5fZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCJjbGljayIsdGhpcy5fY2xpY2tMaXN0ZW5lcikpfSx0LnByb3RvdHlwZS5fb25Nb3VzZU1vdmU9ZnVuY3Rpb24oZSl7dGhpcy5fbGFzdEhvdmVyQ29vcmRzWzBdPT09ZS5wYWdlWCYmdGhpcy5fbGFzdEhvdmVyQ29vcmRzWzFdPT09ZS5wYWdlWXx8KHRoaXMuX29uSG92ZXIoZSksdGhpcy5fbGFzdEhvdmVyQ29vcmRzPVtlLnBhZ2VYLGUucGFnZVldKX0sdC5wcm90b3R5cGUuX29uSG92ZXI9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxyPXRoaXMuX2ZpbmRab25lRXZlbnRBdChlKTtyIT09dGhpcy5fY3VycmVudFpvbmUmJih0aGlzLl9jdXJyZW50Wm9uZSYmKHRoaXMuX2N1cnJlbnRab25lLmxlYXZlQ2FsbGJhY2soKSx0aGlzLl9jdXJyZW50Wm9uZT12b2lkIDAsdGhpcy5fdG9vbHRpcFRpbWVvdXQmJmNsZWFyVGltZW91dCh0aGlzLl90b29sdGlwVGltZW91dCkpLHImJih0aGlzLl9jdXJyZW50Wm9uZT1yLHIuaG92ZXJDYWxsYmFjayYmci5ob3ZlckNhbGxiYWNrKGUpLHRoaXMuX3Rvb2x0aXBUaW1lb3V0PXdpbmRvdy5zZXRUaW1lb3V0KChmdW5jdGlvbigpe3JldHVybiB0Ll9vblRvb2x0aXAoZSl9KSx0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmxpbmtUb29sdGlwSG92ZXJEdXJhdGlvbikpKX0sdC5wcm90b3R5cGUuX29uVG9vbHRpcD1mdW5jdGlvbihlKXt0aGlzLl90b29sdGlwVGltZW91dD12b2lkIDA7dmFyIHQ9dGhpcy5fZmluZFpvbmVFdmVudEF0KGUpO251bGw9PXR8fHQudG9vbHRpcENhbGxiYWNrKGUpfSx0LnByb3RvdHlwZS5fb25Nb3VzZURvd249ZnVuY3Rpb24oZSl7aWYodGhpcy5faW5pdGlhbFNlbGVjdGlvbkxlbmd0aD10aGlzLl9nZXRTZWxlY3Rpb25MZW5ndGgoKSx0aGlzLl9hcmVab25lc0FjdGl2ZSl7dmFyIHQ9dGhpcy5fZmluZFpvbmVFdmVudEF0KGUpOyhudWxsPT10P3ZvaWQgMDp0LndpbGxMaW5rQWN0aXZhdGUoZSkpJiYoZS5wcmV2ZW50RGVmYXVsdCgpLGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCkpfX0sdC5wcm90b3R5cGUuX29uTW91c2VMZWF2ZT1mdW5jdGlvbihlKXt0aGlzLl9jdXJyZW50Wm9uZSYmKHRoaXMuX2N1cnJlbnRab25lLmxlYXZlQ2FsbGJhY2soKSx0aGlzLl9jdXJyZW50Wm9uZT12b2lkIDAsdGhpcy5fdG9vbHRpcFRpbWVvdXQmJmNsZWFyVGltZW91dCh0aGlzLl90b29sdGlwVGltZW91dCkpfSx0LnByb3RvdHlwZS5fb25DbGljaz1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9maW5kWm9uZUV2ZW50QXQoZSkscj10aGlzLl9nZXRTZWxlY3Rpb25MZW5ndGgoKTt0JiZyPT09dGhpcy5faW5pdGlhbFNlbGVjdGlvbkxlbmd0aCYmKHQuY2xpY2tDYWxsYmFjayhlKSxlLnByZXZlbnREZWZhdWx0KCksZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKSl9LHQucHJvdG90eXBlLl9nZXRTZWxlY3Rpb25MZW5ndGg9ZnVuY3Rpb24oKXt2YXIgZT10aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLnNlbGVjdGlvblRleHQ7cmV0dXJuIGU/ZS5sZW5ndGg6MH0sdC5wcm90b3R5cGUuX2ZpbmRab25lRXZlbnRBdD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9tb3VzZVNlcnZpY2UuZ2V0Q29vcmRzKGUsdGhpcy5fc2NyZWVuRWxlbWVudCx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKTtpZih0KWZvcih2YXIgcj10WzBdLGk9dFsxXSxuPTA7bjx0aGlzLl96b25lcy5sZW5ndGg7bisrKXt2YXIgbz10aGlzLl96b25lc1tuXTtpZihvLnkxPT09by55Mil7aWYoaT09PW8ueTEmJnI+PW8ueDEmJnI8by54MilyZXR1cm4gb31lbHNlIGlmKGk9PT1vLnkxJiZyPj1vLngxfHxpPT09by55MiYmcjxvLngyfHxpPm8ueTEmJmk8by55MilyZXR1cm4gb319LG8oW3MoMix1LklCdWZmZXJTZXJ2aWNlKSxzKDMsbC5JTW91c2VTZXJ2aWNlKSxzKDQsbC5JU2VsZWN0aW9uU2VydmljZSkscyg1LHUuSU9wdGlvbnNTZXJ2aWNlKV0sdCl9KGEuRGlzcG9zYWJsZSk7dC5Nb3VzZVpvbmVNYW5hZ2VyPWh9LDYxOTM6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5SZW5kZXJEZWJvdW5jZXI9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9yZW5kZXJDYWxsYmFjaz1lfXJldHVybiBlLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5fYW5pbWF0aW9uRnJhbWUmJih3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUodGhpcy5fYW5pbWF0aW9uRnJhbWUpLHRoaXMuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMCl9LGUucHJvdG90eXBlLnJlZnJlc2g9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXM7dGhpcy5fcm93Q291bnQ9cixlPXZvaWQgMCE9PWU/ZTowLHQ9dm9pZCAwIT09dD90OnRoaXMuX3Jvd0NvdW50LTEsdGhpcy5fcm93U3RhcnQ9dm9pZCAwIT09dGhpcy5fcm93U3RhcnQ/TWF0aC5taW4odGhpcy5fcm93U3RhcnQsZSk6ZSx0aGlzLl9yb3dFbmQ9dm9pZCAwIT09dGhpcy5fcm93RW5kP01hdGgubWF4KHRoaXMuX3Jvd0VuZCx0KTp0LHRoaXMuX2FuaW1hdGlvbkZyYW1lfHwodGhpcy5fYW5pbWF0aW9uRnJhbWU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoZnVuY3Rpb24oKXtyZXR1cm4gaS5faW5uZXJSZWZyZXNoKCl9KSkpfSxlLnByb3RvdHlwZS5faW5uZXJSZWZyZXNoPWZ1bmN0aW9uKCl7aWYodm9pZCAwIT09dGhpcy5fcm93U3RhcnQmJnZvaWQgMCE9PXRoaXMuX3Jvd0VuZCYmdm9pZCAwIT09dGhpcy5fcm93Q291bnQpe3ZhciBlPU1hdGgubWF4KHRoaXMuX3Jvd1N0YXJ0LDApLHQ9TWF0aC5taW4odGhpcy5fcm93RW5kLHRoaXMuX3Jvd0NvdW50LTEpO3RoaXMuX3Jvd1N0YXJ0PXZvaWQgMCx0aGlzLl9yb3dFbmQ9dm9pZCAwLHRoaXMuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMCx0aGlzLl9yZW5kZXJDYWxsYmFjayhlLHQpfX0sZX0oKTt0LlJlbmRlckRlYm91bmNlcj1yfSw1NTk2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlNjcmVlbkRwck1vbml0b3I9dm9pZCAwO3ZhciBvPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQoKXt2YXIgdD1udWxsIT09ZSYmZS5hcHBseSh0aGlzLGFyZ3VtZW50cyl8fHRoaXM7cmV0dXJuIHQuX2N1cnJlbnREZXZpY2VQaXhlbFJhdGlvPXdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHR9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5zZXRMaXN0ZW5lcj1mdW5jdGlvbihlKXt2YXIgdD10aGlzO3RoaXMuX2xpc3RlbmVyJiZ0aGlzLmNsZWFyTGlzdGVuZXIoKSx0aGlzLl9saXN0ZW5lcj1lLHRoaXMuX291dGVyTGlzdGVuZXI9ZnVuY3Rpb24oKXt0Ll9saXN0ZW5lciYmKHQuX2xpc3RlbmVyKHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHQuX2N1cnJlbnREZXZpY2VQaXhlbFJhdGlvKSx0Ll91cGRhdGVEcHIoKSl9LHRoaXMuX3VwZGF0ZURwcigpfSx0LnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7ZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpLHRoaXMuY2xlYXJMaXN0ZW5lcigpfSx0LnByb3RvdHlwZS5fdXBkYXRlRHByPWZ1bmN0aW9uKCl7dmFyIGU7dGhpcy5fb3V0ZXJMaXN0ZW5lciYmKG51bGw9PT0oZT10aGlzLl9yZXNvbHV0aW9uTWVkaWFNYXRjaExpc3QpfHx2b2lkIDA9PT1lfHxlLnJlbW92ZUxpc3RlbmVyKHRoaXMuX291dGVyTGlzdGVuZXIpLHRoaXMuX2N1cnJlbnREZXZpY2VQaXhlbFJhdGlvPXdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHRoaXMuX3Jlc29sdXRpb25NZWRpYU1hdGNoTGlzdD13aW5kb3cubWF0Y2hNZWRpYSgic2NyZWVuIGFuZCAocmVzb2x1dGlvbjogIit3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbysiZHBweCkiKSx0aGlzLl9yZXNvbHV0aW9uTWVkaWFNYXRjaExpc3QuYWRkTGlzdGVuZXIodGhpcy5fb3V0ZXJMaXN0ZW5lcikpfSx0LnByb3RvdHlwZS5jbGVhckxpc3RlbmVyPWZ1bmN0aW9uKCl7dGhpcy5fcmVzb2x1dGlvbk1lZGlhTWF0Y2hMaXN0JiZ0aGlzLl9saXN0ZW5lciYmdGhpcy5fb3V0ZXJMaXN0ZW5lciYmKHRoaXMuX3Jlc29sdXRpb25NZWRpYU1hdGNoTGlzdC5yZW1vdmVMaXN0ZW5lcih0aGlzLl9vdXRlckxpc3RlbmVyKSx0aGlzLl9yZXNvbHV0aW9uTWVkaWFNYXRjaExpc3Q9dm9pZCAwLHRoaXMuX2xpc3RlbmVyPXZvaWQgMCx0aGlzLl9vdXRlckxpc3RlbmVyPXZvaWQgMCl9LHR9KHIoODQ0KS5EaXNwb3NhYmxlKTt0LlNjcmVlbkRwck1vbml0b3I9b30sMzIzNjpmdW5jdGlvbihlLHQscil7dmFyIGksbj10aGlzJiZ0aGlzLl9fZXh0ZW5kc3x8KGk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gaT1PYmplY3Quc2V0UHJvdG90eXBlT2Z8fHtfX3Byb3RvX186W119aW5zdGFuY2VvZiBBcnJheSYmZnVuY3Rpb24oZSx0KXtlLl9fcHJvdG9fXz10fXx8ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHIgaW4gdClPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodCxyKSYmKGVbcl09dFtyXSl9LGkoZSx0KX0sZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgdCYmbnVsbCE9PXQpdGhyb3cgbmV3IFR5cGVFcnJvcigiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSAiK1N0cmluZyh0KSsiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGwiKTtmdW5jdGlvbiByKCl7dGhpcy5jb25zdHJ1Y3Rvcj1lfWkoZSx0KSxlLnByb3RvdHlwZT1udWxsPT09dD9PYmplY3QuY3JlYXRlKHQpOihyLnByb3RvdHlwZT10LnByb3RvdHlwZSxuZXcgcil9KTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5UZXJtaW5hbD12b2lkIDA7dmFyIG89cigyOTUwKSxzPXIoMTY4MCksYT1yKDM2MTQpLGM9cigyNTg0KSxsPXIoNTQzNSksdT1yKDM1MjUpLGg9cigzNTUxKSxmPXIoOTMxMiksXz1yKDYxMTQpLGQ9cigzNjU2KSxwPXIoOTA0Miksdj1yKDM1NyksZz1yKDY5NTQpLHk9cig0NTY3KSxtPXIoMTI5NiksYj1yKDczOTkpLFM9cig4NDYwKSxDPXIoODQzNyksdz1yKDU2ODApLEw9cigzMjMwKSxFPXIoNDcyNSkseD1yKDQyOCksQT1yKDg5MzQpLGs9cig2NDY1KSxNPXIoNTExNCksUj1yKDg5NjkpLFQ9cig0Nzc0KSxPPXIoNDI2OSksQj1yKDU5NDEpLEQ9InVuZGVmaW5lZCIhPXR5cGVvZiB3aW5kb3c/d2luZG93LmRvY3VtZW50Om51bGwsUD1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQpe3ZvaWQgMD09PXQmJih0PXt9KTt2YXIgcj1lLmNhbGwodGhpcyx0KXx8dGhpcztyZXR1cm4gci5icm93c2VyPV8sci5fa2V5RG93bkhhbmRsZWQ9ITEsci5fa2V5UHJlc3NIYW5kbGVkPSExLHIuX3VucHJvY2Vzc2VkRGVhZEtleT0hMSxyLl9vbkN1cnNvck1vdmU9bmV3IFMuRXZlbnRFbWl0dGVyLHIuX29uS2V5PW5ldyBTLkV2ZW50RW1pdHRlcixyLl9vblJlbmRlcj1uZXcgUy5FdmVudEVtaXR0ZXIsci5fb25TZWxlY3Rpb25DaGFuZ2U9bmV3IFMuRXZlbnRFbWl0dGVyLHIuX29uVGl0bGVDaGFuZ2U9bmV3IFMuRXZlbnRFbWl0dGVyLHIuX29uQmVsbD1uZXcgUy5FdmVudEVtaXR0ZXIsci5fb25Gb2N1cz1uZXcgUy5FdmVudEVtaXR0ZXIsci5fb25CbHVyPW5ldyBTLkV2ZW50RW1pdHRlcixyLl9vbkExMXlDaGFyRW1pdHRlcj1uZXcgUy5FdmVudEVtaXR0ZXIsci5fb25BMTF5VGFiRW1pdHRlcj1uZXcgUy5FdmVudEVtaXR0ZXIsci5fc2V0dXAoKSxyLmxpbmtpZmllcj1yLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShoLkxpbmtpZmllciksci5saW5raWZpZXIyPXIucmVnaXN0ZXIoci5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2Uoay5MaW5raWZpZXIyKSksci5yZWdpc3RlcihyLl9pbnB1dEhhbmRsZXIub25SZXF1ZXN0QmVsbCgoZnVuY3Rpb24oKXtyZXR1cm4gci5iZWxsKCl9KSkpLHIucmVnaXN0ZXIoci5faW5wdXRIYW5kbGVyLm9uUmVxdWVzdFJlZnJlc2hSb3dzKChmdW5jdGlvbihlLHQpe3JldHVybiByLnJlZnJlc2goZSx0KX0pKSksci5yZWdpc3RlcihyLl9pbnB1dEhhbmRsZXIub25SZXF1ZXN0U2VuZEZvY3VzKChmdW5jdGlvbigpe3JldHVybiByLl9yZXBvcnRGb2N1cygpfSkpKSxyLnJlZ2lzdGVyKHIuX2lucHV0SGFuZGxlci5vblJlcXVlc3RSZXNldCgoZnVuY3Rpb24oKXtyZXR1cm4gci5yZXNldCgpfSkpKSxyLnJlZ2lzdGVyKHIuX2lucHV0SGFuZGxlci5vblJlcXVlc3RXaW5kb3dzT3B0aW9uc1JlcG9ydCgoZnVuY3Rpb24oZSl7cmV0dXJuIHIuX3JlcG9ydFdpbmRvd3NPcHRpb25zKGUpfSkpKSxyLnJlZ2lzdGVyKHIuX2lucHV0SGFuZGxlci5vbkNvbG9yKChmdW5jdGlvbihlKXtyZXR1cm4gci5faGFuZGxlQ29sb3JFdmVudChlKX0pKSksci5yZWdpc3RlcigoMCxTLmZvcndhcmRFdmVudCkoci5faW5wdXRIYW5kbGVyLm9uQ3Vyc29yTW92ZSxyLl9vbkN1cnNvck1vdmUpKSxyLnJlZ2lzdGVyKCgwLFMuZm9yd2FyZEV2ZW50KShyLl9pbnB1dEhhbmRsZXIub25UaXRsZUNoYW5nZSxyLl9vblRpdGxlQ2hhbmdlKSksci5yZWdpc3RlcigoMCxTLmZvcndhcmRFdmVudCkoci5faW5wdXRIYW5kbGVyLm9uQTExeUNoYXIsci5fb25BMTF5Q2hhckVtaXR0ZXIpKSxyLnJlZ2lzdGVyKCgwLFMuZm9yd2FyZEV2ZW50KShyLl9pbnB1dEhhbmRsZXIub25BMTF5VGFiLHIuX29uQTExeVRhYkVtaXR0ZXIpKSxyLnJlZ2lzdGVyKHIuX2J1ZmZlclNlcnZpY2Uub25SZXNpemUoKGZ1bmN0aW9uKGUpe3JldHVybiByLl9hZnRlclJlc2l6ZShlLmNvbHMsZS5yb3dzKX0pKSkscn1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25DdXJzb3JNb3ZlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uQ3Vyc29yTW92ZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uS2V5Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uS2V5LmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZW5kZXIiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZW5kZXIuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblNlbGVjdGlvbkNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblNlbGVjdGlvbkNoYW5nZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uVGl0bGVDaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25UaXRsZUNoYW5nZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQmVsbCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkJlbGwuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkZvY3VzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uRm9jdXMuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkJsdXIiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25CbHVyLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25BMTF5Q2hhciIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkExMXlDaGFyRW1pdHRlci5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQTExeVRhYiIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkExMXlUYWJFbWl0dGVyLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLHQucHJvdG90eXBlLl9oYW5kbGVDb2xvckV2ZW50PWZ1bmN0aW9uKGUpe3ZhciB0LHI7aWYodGhpcy5fY29sb3JNYW5hZ2VyKXtmb3IodmFyIGk9MCxuPWU7aTxuLmxlbmd0aDtpKyspe3ZhciBvPW5baV0scz12b2lkIDAsYT0iIjtzd2l0Y2goby5pbmRleCl7Y2FzZSAyNTY6cz0iZm9yZWdyb3VuZCIsYT0iMTAiO2JyZWFrO2Nhc2UgMjU3OnM9ImJhY2tncm91bmQiLGE9IjExIjticmVhaztjYXNlIDI1ODpzPSJjdXJzb3IiLGE9IjEyIjticmVhaztkZWZhdWx0OnM9ImFuc2kiLGE9IjQ7IitvLmluZGV4fWlmKHMpc3dpdGNoKG8udHlwZSl7Y2FzZSAwOnZhciBsPVQuY29sb3IudG9Db2xvclJHQigiYW5zaSI9PT1zP3RoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMuYW5zaVtvLmluZGV4XTp0aGlzLl9jb2xvck1hbmFnZXIuY29sb3JzW3NdKTt0aGlzLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoYy5DMC5FU0MrIl0iK2ErIjsiKygwLEIudG9SZ2JTdHJpbmcpKGwpK2MuQzAuQkVMKTticmVhaztjYXNlIDE6ImFuc2kiPT09cz90aGlzLl9jb2xvck1hbmFnZXIuY29sb3JzLmFuc2lbby5pbmRleF09VC5yZ2JhLnRvQ29sb3IuYXBwbHkoVC5yZ2JhLG8uY29sb3IpOnRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnNbc109VC5yZ2JhLnRvQ29sb3IuYXBwbHkoVC5yZ2JhLG8uY29sb3IpO2JyZWFrO2Nhc2UgMjp0aGlzLl9jb2xvck1hbmFnZXIucmVzdG9yZUNvbG9yKG8uaW5kZXgpfX1udWxsPT09KHQ9dGhpcy5fcmVuZGVyU2VydmljZSl8fHZvaWQgMD09PXR8fHQuc2V0Q29sb3JzKHRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMpLG51bGw9PT0ocj10aGlzLnZpZXdwb3J0KXx8dm9pZCAwPT09cnx8ci5vblRoZW1lQ2hhbmdlKHRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMpfX0sdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3ZhciB0LHIsaTt0aGlzLl9pc0Rpc3Bvc2VkfHwoZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpLG51bGw9PT0odD10aGlzLl9yZW5kZXJTZXJ2aWNlKXx8dm9pZCAwPT09dHx8dC5kaXNwb3NlKCksdGhpcy5fY3VzdG9tS2V5RXZlbnRIYW5kbGVyPXZvaWQgMCx0aGlzLndyaXRlPWZ1bmN0aW9uKCl7fSxudWxsPT09KGk9bnVsbD09PShyPXRoaXMuZWxlbWVudCl8fHZvaWQgMD09PXI/dm9pZCAwOnIucGFyZW50Tm9kZSl8fHZvaWQgMD09PWl8fGkucmVtb3ZlQ2hpbGQodGhpcy5lbGVtZW50KSl9LHQucHJvdG90eXBlLl9zZXR1cD1mdW5jdGlvbigpe2UucHJvdG90eXBlLl9zZXR1cC5jYWxsKHRoaXMpLHRoaXMuX2N1c3RvbUtleUV2ZW50SGFuZGxlcj12b2lkIDB9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwiYnVmZmVyIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYnVmZmVycy5hY3RpdmV9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZm9jdXM9ZnVuY3Rpb24oKXt0aGlzLnRleHRhcmVhJiZ0aGlzLnRleHRhcmVhLmZvY3VzKHtwcmV2ZW50U2Nyb2xsOiEwfSl9LHQucHJvdG90eXBlLl91cGRhdGVPcHRpb25zPWZ1bmN0aW9uKHQpe3ZhciByLGksbixvO3N3aXRjaChlLnByb3RvdHlwZS5fdXBkYXRlT3B0aW9ucy5jYWxsKHRoaXMsdCksdCl7Y2FzZSJmb250RmFtaWx5IjpjYXNlImZvbnRTaXplIjpudWxsPT09KHI9dGhpcy5fcmVuZGVyU2VydmljZSl8fHZvaWQgMD09PXJ8fHIuY2xlYXIoKSxudWxsPT09KGk9dGhpcy5fY2hhclNpemVTZXJ2aWNlKXx8dm9pZCAwPT09aXx8aS5tZWFzdXJlKCk7YnJlYWs7Y2FzZSJjdXJzb3JCbGluayI6Y2FzZSJjdXJzb3JTdHlsZSI6dGhpcy5yZWZyZXNoKHRoaXMuYnVmZmVyLnksdGhpcy5idWZmZXIueSk7YnJlYWs7Y2FzZSJjdXN0b21HbHlwaHMiOmNhc2UiZHJhd0JvbGRUZXh0SW5CcmlnaHRDb2xvcnMiOmNhc2UibGV0dGVyU3BhY2luZyI6Y2FzZSJsaW5lSGVpZ2h0IjpjYXNlImZvbnRXZWlnaHQiOmNhc2UiZm9udFdlaWdodEJvbGQiOmNhc2UibWluaW11bUNvbnRyYXN0UmF0aW8iOnRoaXMuX3JlbmRlclNlcnZpY2UmJih0aGlzLl9yZW5kZXJTZXJ2aWNlLmNsZWFyKCksdGhpcy5fcmVuZGVyU2VydmljZS5vblJlc2l6ZSh0aGlzLmNvbHMsdGhpcy5yb3dzKSx0aGlzLnJlZnJlc2goMCx0aGlzLnJvd3MtMSkpO2JyZWFrO2Nhc2UicmVuZGVyZXJUeXBlIjp0aGlzLl9yZW5kZXJTZXJ2aWNlJiYodGhpcy5fcmVuZGVyU2VydmljZS5zZXRSZW5kZXJlcih0aGlzLl9jcmVhdGVSZW5kZXJlcigpKSx0aGlzLl9yZW5kZXJTZXJ2aWNlLm9uUmVzaXplKHRoaXMuY29scyx0aGlzLnJvd3MpKTticmVhaztjYXNlInNjcm9sbGJhY2siOm51bGw9PT0obj10aGlzLnZpZXdwb3J0KXx8dm9pZCAwPT09bnx8bi5zeW5jU2Nyb2xsQXJlYSgpO2JyZWFrO2Nhc2Uic2NyZWVuUmVhZGVyTW9kZSI6dGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcmVlblJlYWRlck1vZGU/IXRoaXMuX2FjY2Vzc2liaWxpdHlNYW5hZ2VyJiZ0aGlzLl9yZW5kZXJTZXJ2aWNlJiYodGhpcy5fYWNjZXNzaWJpbGl0eU1hbmFnZXI9bmV3IHkuQWNjZXNzaWJpbGl0eU1hbmFnZXIodGhpcyx0aGlzLl9yZW5kZXJTZXJ2aWNlKSk6KG51bGw9PT0obz10aGlzLl9hY2Nlc3NpYmlsaXR5TWFuYWdlcil8fHZvaWQgMD09PW98fG8uZGlzcG9zZSgpLHRoaXMuX2FjY2Vzc2liaWxpdHlNYW5hZ2VyPXZvaWQgMCk7YnJlYWs7Y2FzZSJ0YWJTdG9wV2lkdGgiOnRoaXMuYnVmZmVycy5zZXR1cFRhYlN0b3BzKCk7YnJlYWs7Y2FzZSJ0aGVtZSI6dGhpcy5fc2V0VGhlbWUodGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zLnRoZW1lKX19LHQucHJvdG90eXBlLl9vblRleHRBcmVhRm9jdXM9ZnVuY3Rpb24oZSl7dGhpcy5jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuc2VuZEZvY3VzJiZ0aGlzLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoYy5DMC5FU0MrIltJIiksdGhpcy51cGRhdGVDdXJzb3JTdHlsZShlKSx0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LmFkZCgiZm9jdXMiKSx0aGlzLl9zaG93Q3Vyc29yKCksdGhpcy5fb25Gb2N1cy5maXJlKCl9LHQucHJvdG90eXBlLmJsdXI9ZnVuY3Rpb24oKXt2YXIgZTtyZXR1cm4gbnVsbD09PShlPXRoaXMudGV4dGFyZWEpfHx2b2lkIDA9PT1lP3ZvaWQgMDplLmJsdXIoKX0sdC5wcm90b3R5cGUuX29uVGV4dEFyZWFCbHVyPWZ1bmN0aW9uKCl7dGhpcy50ZXh0YXJlYS52YWx1ZT0iIix0aGlzLnJlZnJlc2godGhpcy5idWZmZXIueSx0aGlzLmJ1ZmZlci55KSx0aGlzLmNvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5zZW5kRm9jdXMmJnRoaXMuY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChjLkMwLkVTQysiW08iKSx0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgiZm9jdXMiKSx0aGlzLl9vbkJsdXIuZmlyZSgpfSx0LnByb3RvdHlwZS5fc3luY1RleHRBcmVhPWZ1bmN0aW9uKCl7aWYodGhpcy50ZXh0YXJlYSYmdGhpcy5idWZmZXIuaXNDdXJzb3JJblZpZXdwb3J0JiYhdGhpcy5fY29tcG9zaXRpb25IZWxwZXIuaXNDb21wb3NpbmcmJnRoaXMuX3JlbmRlclNlcnZpY2Upe3ZhciBlPXRoaXMuYnVmZmVyLnliYXNlK3RoaXMuYnVmZmVyLnksdD10aGlzLmJ1ZmZlci5saW5lcy5nZXQoZSk7aWYodCl7dmFyIHI9TWF0aC5taW4odGhpcy5idWZmZXIueCx0aGlzLmNvbHMtMSksaT10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbEhlaWdodCxuPXQuZ2V0V2lkdGgociksbz10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoKm4scz10aGlzLmJ1ZmZlci55KnRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0LGE9cip0aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoO3RoaXMudGV4dGFyZWEuc3R5bGUubGVmdD1hKyJweCIsdGhpcy50ZXh0YXJlYS5zdHlsZS50b3A9cysicHgiLHRoaXMudGV4dGFyZWEuc3R5bGUud2lkdGg9bysicHgiLHRoaXMudGV4dGFyZWEuc3R5bGUuaGVpZ2h0PWkrInB4Iix0aGlzLnRleHRhcmVhLnN0eWxlLmxpbmVIZWlnaHQ9aSsicHgiLHRoaXMudGV4dGFyZWEuc3R5bGUuekluZGV4PSItNSJ9fX0sdC5wcm90b3R5cGUuX2luaXRHbG9iYWw9ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3RoaXMuX2JpbmRLZXlzKCksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy5lbGVtZW50LCJjb3B5IiwoZnVuY3Rpb24odCl7ZS5oYXNTZWxlY3Rpb24oKSYmKDAsYS5jb3B5SGFuZGxlcikodCxlLl9zZWxlY3Rpb25TZXJ2aWNlKX0pKSk7dmFyIHQ9ZnVuY3Rpb24odCl7cmV0dXJuKDAsYS5oYW5kbGVQYXN0ZUV2ZW50KSh0LGUudGV4dGFyZWEsZS5jb3JlU2VydmljZSl9O3RoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsInBhc3RlIix0KSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy5lbGVtZW50LCJwYXN0ZSIsdCkpLF8uaXNGaXJlZm94P3RoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMuZWxlbWVudCwibW91c2Vkb3duIiwoZnVuY3Rpb24odCl7Mj09PXQuYnV0dG9uJiYoMCxhLnJpZ2h0Q2xpY2tIYW5kbGVyKSh0LGUudGV4dGFyZWEsZS5zY3JlZW5FbGVtZW50LGUuX3NlbGVjdGlvblNlcnZpY2UsZS5vcHRpb25zLnJpZ2h0Q2xpY2tTZWxlY3RzV29yZCl9KSkpOnRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMuZWxlbWVudCwiY29udGV4dG1lbnUiLChmdW5jdGlvbih0KXsoMCxhLnJpZ2h0Q2xpY2tIYW5kbGVyKSh0LGUudGV4dGFyZWEsZS5zY3JlZW5FbGVtZW50LGUuX3NlbGVjdGlvblNlcnZpY2UsZS5vcHRpb25zLnJpZ2h0Q2xpY2tTZWxlY3RzV29yZCl9KSkpLF8uaXNMaW51eCYmdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy5lbGVtZW50LCJhdXhjbGljayIsKGZ1bmN0aW9uKHQpezE9PT10LmJ1dHRvbiYmKDAsYS5tb3ZlVGV4dEFyZWFVbmRlck1vdXNlQ3Vyc29yKSh0LGUudGV4dGFyZWEsZS5zY3JlZW5FbGVtZW50KX0pKSl9LHQucHJvdG90eXBlLl9iaW5kS2V5cz1mdW5jdGlvbigpe3ZhciBlPXRoaXM7dGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy50ZXh0YXJlYSwia2V5dXAiLChmdW5jdGlvbih0KXtyZXR1cm4gZS5fa2V5VXAodCl9KSwhMCkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImtleWRvd24iLChmdW5jdGlvbih0KXtyZXR1cm4gZS5fa2V5RG93bih0KX0pLCEwKSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy50ZXh0YXJlYSwia2V5cHJlc3MiLChmdW5jdGlvbih0KXtyZXR1cm4gZS5fa2V5UHJlc3ModCl9KSwhMCkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImNvbXBvc2l0aW9uc3RhcnQiLChmdW5jdGlvbigpe3JldHVybiBlLl9jb21wb3NpdGlvbkhlbHBlci5jb21wb3NpdGlvbnN0YXJ0KCl9KSkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImNvbXBvc2l0aW9udXBkYXRlIiwoZnVuY3Rpb24odCl7cmV0dXJuIGUuX2NvbXBvc2l0aW9uSGVscGVyLmNvbXBvc2l0aW9udXBkYXRlKHQpfSkpKSx0aGlzLnJlZ2lzdGVyKCgwLGQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLnRleHRhcmVhLCJjb21wb3NpdGlvbmVuZCIsKGZ1bmN0aW9uKCl7cmV0dXJuIGUuX2NvbXBvc2l0aW9uSGVscGVyLmNvbXBvc2l0aW9uZW5kKCl9KSkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImlucHV0IiwoZnVuY3Rpb24odCl7cmV0dXJuIGUuX2lucHV0RXZlbnQodCl9KSwhMCkpLHRoaXMucmVnaXN0ZXIodGhpcy5vblJlbmRlcigoZnVuY3Rpb24oKXtyZXR1cm4gZS5fY29tcG9zaXRpb25IZWxwZXIudXBkYXRlQ29tcG9zaXRpb25FbGVtZW50cygpfSkpKSx0aGlzLnJlZ2lzdGVyKHRoaXMub25SZW5kZXIoKGZ1bmN0aW9uKHQpe3JldHVybiBlLl9xdWV1ZUxpbmtpZmljYXRpb24odC5zdGFydCx0LmVuZCl9KSkpfSx0LnByb3RvdHlwZS5vcGVuPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7aWYoIWUpdGhyb3cgbmV3IEVycm9yKCJUZXJtaW5hbCByZXF1aXJlcyBhIHBhcmVudCBlbGVtZW50LiIpO2UuaXNDb25uZWN0ZWR8fHRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIlRlcm1pbmFsLm9wZW4gd2FzIGNhbGxlZCBvbiBhbiBlbGVtZW50IHRoYXQgd2FzIG5vdCBhdHRhY2hlZCB0byB0aGUgRE9NIiksdGhpcy5fZG9jdW1lbnQ9ZS5vd25lckRvY3VtZW50LHRoaXMuZWxlbWVudD10aGlzLl9kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKSx0aGlzLmVsZW1lbnQuZGlyPSJsdHIiLHRoaXMuZWxlbWVudC5jbGFzc0xpc3QuYWRkKCJ0ZXJtaW5hbCIpLHRoaXMuZWxlbWVudC5jbGFzc0xpc3QuYWRkKCJ4dGVybSIpLHRoaXMuZWxlbWVudC5zZXRBdHRyaWJ1dGUoInRhYmluZGV4IiwiMCIpLGUuYXBwZW5kQ2hpbGQodGhpcy5lbGVtZW50KTt2YXIgcj1ELmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTt0aGlzLl92aWV3cG9ydEVsZW1lbnQ9RC5jcmVhdGVFbGVtZW50KCJkaXYiKSx0aGlzLl92aWV3cG9ydEVsZW1lbnQuY2xhc3NMaXN0LmFkZCgieHRlcm0tdmlld3BvcnQiKSxyLmFwcGVuZENoaWxkKHRoaXMuX3ZpZXdwb3J0RWxlbWVudCksdGhpcy5fdmlld3BvcnRTY3JvbGxBcmVhPUQuY3JlYXRlRWxlbWVudCgiZGl2IiksdGhpcy5fdmlld3BvcnRTY3JvbGxBcmVhLmNsYXNzTGlzdC5hZGQoInh0ZXJtLXNjcm9sbC1hcmVhIiksdGhpcy5fdmlld3BvcnRFbGVtZW50LmFwcGVuZENoaWxkKHRoaXMuX3ZpZXdwb3J0U2Nyb2xsQXJlYSksdGhpcy5zY3JlZW5FbGVtZW50PUQuY3JlYXRlRWxlbWVudCgiZGl2IiksdGhpcy5zY3JlZW5FbGVtZW50LmNsYXNzTGlzdC5hZGQoInh0ZXJtLXNjcmVlbiIpLHRoaXMuX2hlbHBlckNvbnRhaW5lcj1ELmNyZWF0ZUVsZW1lbnQoImRpdiIpLHRoaXMuX2hlbHBlckNvbnRhaW5lci5jbGFzc0xpc3QuYWRkKCJ4dGVybS1oZWxwZXJzIiksdGhpcy5zY3JlZW5FbGVtZW50LmFwcGVuZENoaWxkKHRoaXMuX2hlbHBlckNvbnRhaW5lciksci5hcHBlbmRDaGlsZCh0aGlzLnNjcmVlbkVsZW1lbnQpLHRoaXMudGV4dGFyZWE9RC5jcmVhdGVFbGVtZW50KCJ0ZXh0YXJlYSIpLHRoaXMudGV4dGFyZWEuY2xhc3NMaXN0LmFkZCgieHRlcm0taGVscGVyLXRleHRhcmVhIiksdGhpcy50ZXh0YXJlYS5zZXRBdHRyaWJ1dGUoImFyaWEtbGFiZWwiLHAucHJvbXB0TGFiZWwpLHRoaXMudGV4dGFyZWEuc2V0QXR0cmlidXRlKCJhcmlhLW11bHRpbGluZSIsImZhbHNlIiksdGhpcy50ZXh0YXJlYS5zZXRBdHRyaWJ1dGUoImF1dG9jb3JyZWN0Iiwib2ZmIiksdGhpcy50ZXh0YXJlYS5zZXRBdHRyaWJ1dGUoImF1dG9jYXBpdGFsaXplIiwib2ZmIiksdGhpcy50ZXh0YXJlYS5zZXRBdHRyaWJ1dGUoInNwZWxsY2hlY2siLCJmYWxzZSIpLHRoaXMudGV4dGFyZWEudGFiSW5kZXg9MCx0aGlzLnJlZ2lzdGVyKCgwLGQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLnRleHRhcmVhLCJmb2N1cyIsKGZ1bmN0aW9uKGUpe3JldHVybiB0Ll9vblRleHRBcmVhRm9jdXMoZSl9KSkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImJsdXIiLChmdW5jdGlvbigpe3JldHVybiB0Ll9vblRleHRBcmVhQmx1cigpfSkpKSx0aGlzLl9oZWxwZXJDb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy50ZXh0YXJlYSk7dmFyIGk9dGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2UoTS5Db3JlQnJvd3NlclNlcnZpY2UsdGhpcy50ZXh0YXJlYSk7dGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklDb3JlQnJvd3NlclNlcnZpY2UsaSksdGhpcy5fY2hhclNpemVTZXJ2aWNlPXRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKHguQ2hhclNpemVTZXJ2aWNlLHRoaXMuX2RvY3VtZW50LHRoaXMuX2hlbHBlckNvbnRhaW5lciksdGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklDaGFyU2l6ZVNlcnZpY2UsdGhpcy5fY2hhclNpemVTZXJ2aWNlKSx0aGlzLl90aGVtZT10aGlzLm9wdGlvbnMudGhlbWV8fHRoaXMuX3RoZW1lLHRoaXMuX2NvbG9yTWFuYWdlcj1uZXcgdy5Db2xvck1hbmFnZXIoRCx0aGlzLm9wdGlvbnMuYWxsb3dUcmFuc3BhcmVuY3kpLHRoaXMucmVnaXN0ZXIodGhpcy5vcHRpb25zU2VydmljZS5vbk9wdGlvbkNoYW5nZSgoZnVuY3Rpb24oZSl7cmV0dXJuIHQuX2NvbG9yTWFuYWdlci5vbk9wdGlvbnNDaGFuZ2UoZSl9KSkpLHRoaXMuX2NvbG9yTWFuYWdlci5zZXRUaGVtZSh0aGlzLl90aGVtZSksdGhpcy5fY2hhcmFjdGVySm9pbmVyU2VydmljZT10aGlzLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShPLkNoYXJhY3RlckpvaW5lclNlcnZpY2UpLHRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2UoRS5JQ2hhcmFjdGVySm9pbmVyU2VydmljZSx0aGlzLl9jaGFyYWN0ZXJKb2luZXJTZXJ2aWNlKTt2YXIgbj10aGlzLl9jcmVhdGVSZW5kZXJlcigpO3RoaXMuX3JlbmRlclNlcnZpY2U9dGhpcy5yZWdpc3Rlcih0aGlzLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShMLlJlbmRlclNlcnZpY2Usbix0aGlzLnJvd3MsdGhpcy5zY3JlZW5FbGVtZW50KSksdGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklSZW5kZXJTZXJ2aWNlLHRoaXMuX3JlbmRlclNlcnZpY2UpLHRoaXMucmVnaXN0ZXIodGhpcy5fcmVuZGVyU2VydmljZS5vblJlbmRlcmVkQnVmZmVyQ2hhbmdlKChmdW5jdGlvbihlKXtyZXR1cm4gdC5fb25SZW5kZXIuZmlyZShlKX0pKSksdGhpcy5vblJlc2l6ZSgoZnVuY3Rpb24oZSl7cmV0dXJuIHQuX3JlbmRlclNlcnZpY2UucmVzaXplKGUuY29scyxlLnJvd3MpfSkpLHRoaXMuX2NvbXBvc2l0aW9uVmlldz1ELmNyZWF0ZUVsZW1lbnQoImRpdiIpLHRoaXMuX2NvbXBvc2l0aW9uVmlldy5jbGFzc0xpc3QuYWRkKCJjb21wb3NpdGlvbi12aWV3IiksdGhpcy5fY29tcG9zaXRpb25IZWxwZXI9dGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2Uoby5Db21wb3NpdGlvbkhlbHBlcix0aGlzLnRleHRhcmVhLHRoaXMuX2NvbXBvc2l0aW9uVmlldyksdGhpcy5faGVscGVyQ29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMuX2NvbXBvc2l0aW9uVmlldyksdGhpcy5lbGVtZW50LmFwcGVuZENoaWxkKHIpLHRoaXMuX3NvdW5kU2VydmljZT10aGlzLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZSh2LlNvdW5kU2VydmljZSksdGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklTb3VuZFNlcnZpY2UsdGhpcy5fc291bmRTZXJ2aWNlKSx0aGlzLl9tb3VzZVNlcnZpY2U9dGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2UoQS5Nb3VzZVNlcnZpY2UpLHRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2UoRS5JTW91c2VTZXJ2aWNlLHRoaXMuX21vdXNlU2VydmljZSksdGhpcy52aWV3cG9ydD10aGlzLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShzLlZpZXdwb3J0LChmdW5jdGlvbihlKXtyZXR1cm4gdC5zY3JvbGxMaW5lcyhlLCEwLDEpfSksdGhpcy5fdmlld3BvcnRFbGVtZW50LHRoaXMuX3ZpZXdwb3J0U2Nyb2xsQXJlYSx0aGlzLmVsZW1lbnQpLHRoaXMudmlld3BvcnQub25UaGVtZUNoYW5nZSh0aGlzLl9jb2xvck1hbmFnZXIuY29sb3JzKSx0aGlzLnJlZ2lzdGVyKHRoaXMuX2lucHV0SGFuZGxlci5vblJlcXVlc3RTeW5jU2Nyb2xsQmFyKChmdW5jdGlvbigpe3JldHVybiB0LnZpZXdwb3J0LnN5bmNTY3JvbGxBcmVhKCl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy52aWV3cG9ydCksdGhpcy5yZWdpc3Rlcih0aGlzLm9uQ3Vyc29yTW92ZSgoZnVuY3Rpb24oKXt0Ll9yZW5kZXJTZXJ2aWNlLm9uQ3Vyc29yTW92ZSgpLHQuX3N5bmNUZXh0QXJlYSgpfSkpKSx0aGlzLnJlZ2lzdGVyKHRoaXMub25SZXNpemUoKGZ1bmN0aW9uKCl7cmV0dXJuIHQuX3JlbmRlclNlcnZpY2Uub25SZXNpemUodC5jb2xzLHQucm93cyl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy5vbkJsdXIoKGZ1bmN0aW9uKCl7cmV0dXJuIHQuX3JlbmRlclNlcnZpY2Uub25CbHVyKCl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy5vbkZvY3VzKChmdW5jdGlvbigpe3JldHVybiB0Ll9yZW5kZXJTZXJ2aWNlLm9uRm9jdXMoKX0pKSksdGhpcy5yZWdpc3Rlcih0aGlzLl9yZW5kZXJTZXJ2aWNlLm9uRGltZW5zaW9uc0NoYW5nZSgoZnVuY3Rpb24oKXtyZXR1cm4gdC52aWV3cG9ydC5zeW5jU2Nyb2xsQXJlYSgpfSkpKSx0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlPXRoaXMucmVnaXN0ZXIodGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2UoZi5TZWxlY3Rpb25TZXJ2aWNlLHRoaXMuZWxlbWVudCx0aGlzLnNjcmVlbkVsZW1lbnQsdGhpcy5saW5raWZpZXIyKSksdGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklTZWxlY3Rpb25TZXJ2aWNlLHRoaXMuX3NlbGVjdGlvblNlcnZpY2UpLHRoaXMucmVnaXN0ZXIodGhpcy5fc2VsZWN0aW9uU2VydmljZS5vblJlcXVlc3RTY3JvbGxMaW5lcygoZnVuY3Rpb24oZSl7cmV0dXJuIHQuc2Nyb2xsTGluZXMoZS5hbW91bnQsZS5zdXBwcmVzc1Njcm9sbEV2ZW50KX0pKSksdGhpcy5yZWdpc3Rlcih0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLm9uU2VsZWN0aW9uQ2hhbmdlKChmdW5jdGlvbigpe3JldHVybiB0Ll9vblNlbGVjdGlvbkNoYW5nZS5maXJlKCl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy5fc2VsZWN0aW9uU2VydmljZS5vblJlcXVlc3RSZWRyYXcoKGZ1bmN0aW9uKGUpe3JldHVybiB0Ll9yZW5kZXJTZXJ2aWNlLm9uU2VsZWN0aW9uQ2hhbmdlZChlLnN0YXJ0LGUuZW5kLGUuY29sdW1uU2VsZWN0TW9kZSl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy5fc2VsZWN0aW9uU2VydmljZS5vbkxpbnV4TW91c2VTZWxlY3Rpb24oKGZ1bmN0aW9uKGUpe3QudGV4dGFyZWEudmFsdWU9ZSx0LnRleHRhcmVhLmZvY3VzKCksdC50ZXh0YXJlYS5zZWxlY3QoKX0pKSksdGhpcy5yZWdpc3Rlcih0aGlzLl9vblNjcm9sbC5ldmVudCgoZnVuY3Rpb24oZSl7dC52aWV3cG9ydC5zeW5jU2Nyb2xsQXJlYSgpLHQuX3NlbGVjdGlvblNlcnZpY2UucmVmcmVzaCgpfSkpKSx0aGlzLnJlZ2lzdGVyKCgwLGQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLl92aWV3cG9ydEVsZW1lbnQsInNjcm9sbCIsKGZ1bmN0aW9uKCl7cmV0dXJuIHQuX3NlbGVjdGlvblNlcnZpY2UucmVmcmVzaCgpfSkpKSx0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyPXRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKGcuTW91c2Vab25lTWFuYWdlcix0aGlzLmVsZW1lbnQsdGhpcy5zY3JlZW5FbGVtZW50KSx0aGlzLnJlZ2lzdGVyKHRoaXMuX21vdXNlWm9uZU1hbmFnZXIpLHRoaXMucmVnaXN0ZXIodGhpcy5vblNjcm9sbCgoZnVuY3Rpb24oKXtyZXR1cm4gdC5fbW91c2Vab25lTWFuYWdlci5jbGVhckFsbCgpfSkpKSx0aGlzLmxpbmtpZmllci5hdHRhY2hUb0RvbSh0aGlzLmVsZW1lbnQsdGhpcy5fbW91c2Vab25lTWFuYWdlciksdGhpcy5saW5raWZpZXIyLmF0dGFjaFRvRG9tKHRoaXMuc2NyZWVuRWxlbWVudCx0aGlzLl9tb3VzZVNlcnZpY2UsdGhpcy5fcmVuZGVyU2VydmljZSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy5lbGVtZW50LCJtb3VzZWRvd24iLChmdW5jdGlvbihlKXtyZXR1cm4gdC5fc2VsZWN0aW9uU2VydmljZS5vbk1vdXNlRG93bihlKX0pKSksdGhpcy5jb3JlTW91c2VTZXJ2aWNlLmFyZU1vdXNlRXZlbnRzQWN0aXZlPyh0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLmRpc2FibGUoKSx0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LmFkZCgiZW5hYmxlLW1vdXNlLWV2ZW50cyIpKTp0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLmVuYWJsZSgpLHRoaXMub3B0aW9ucy5zY3JlZW5SZWFkZXJNb2RlJiYodGhpcy5fYWNjZXNzaWJpbGl0eU1hbmFnZXI9bmV3IHkuQWNjZXNzaWJpbGl0eU1hbmFnZXIodGhpcyx0aGlzLl9yZW5kZXJTZXJ2aWNlKSksdGhpcy5fY2hhclNpemVTZXJ2aWNlLm1lYXN1cmUoKSx0aGlzLnJlZnJlc2goMCx0aGlzLnJvd3MtMSksdGhpcy5faW5pdEdsb2JhbCgpLHRoaXMuYmluZE1vdXNlKCl9LHQucHJvdG90eXBlLl9jcmVhdGVSZW5kZXJlcj1mdW5jdGlvbigpe3N3aXRjaCh0aGlzLm9wdGlvbnMucmVuZGVyZXJUeXBlKXtjYXNlImNhbnZhcyI6cmV0dXJuIHRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKHUuUmVuZGVyZXIsdGhpcy5fY29sb3JNYW5hZ2VyLmNvbG9ycyx0aGlzLnNjcmVlbkVsZW1lbnQsdGhpcy5saW5raWZpZXIsdGhpcy5saW5raWZpZXIyKTtjYXNlImRvbSI6cmV0dXJuIHRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKG0uRG9tUmVuZGVyZXIsdGhpcy5fY29sb3JNYW5hZ2VyLmNvbG9ycyx0aGlzLmVsZW1lbnQsdGhpcy5zY3JlZW5FbGVtZW50LHRoaXMuX3ZpZXdwb3J0RWxlbWVudCx0aGlzLmxpbmtpZmllcix0aGlzLmxpbmtpZmllcjIpO2RlZmF1bHQ6dGhyb3cgbmV3IEVycm9yKCdVbnJlY29nbml6ZWQgcmVuZGVyZXJUeXBlICInK3RoaXMub3B0aW9ucy5yZW5kZXJlclR5cGUrJyInKX19LHQucHJvdG90eXBlLl9zZXRUaGVtZT1mdW5jdGlvbihlKXt2YXIgdCxyLGk7dGhpcy5fdGhlbWU9ZSxudWxsPT09KHQ9dGhpcy5fY29sb3JNYW5hZ2VyKXx8dm9pZCAwPT09dHx8dC5zZXRUaGVtZShlKSxudWxsPT09KHI9dGhpcy5fcmVuZGVyU2VydmljZSl8fHZvaWQgMD09PXJ8fHIuc2V0Q29sb3JzKHRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMpLG51bGw9PT0oaT10aGlzLnZpZXdwb3J0KXx8dm9pZCAwPT09aXx8aS5vblRoZW1lQ2hhbmdlKHRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMpfSx0LnByb3RvdHlwZS5iaW5kTW91c2U9ZnVuY3Rpb24oKXt2YXIgZT10aGlzLHQ9dGhpcyxyPXRoaXMuZWxlbWVudDtmdW5jdGlvbiBpKGUpe3ZhciByLGksbj10Ll9tb3VzZVNlcnZpY2UuZ2V0UmF3Qnl0ZUNvb3JkcyhlLHQuc2NyZWVuRWxlbWVudCx0LmNvbHMsdC5yb3dzKTtpZighbilyZXR1cm4hMTtzd2l0Y2goZS5vdmVycmlkZVR5cGV8fGUudHlwZSl7Y2FzZSJtb3VzZW1vdmUiOmk9MzIsdm9pZCAwPT09ZS5idXR0b25zPyhyPTMsdm9pZCAwIT09ZS5idXR0b24mJihyPWUuYnV0dG9uPDM/ZS5idXR0b246MykpOnI9MSZlLmJ1dHRvbnM/MDo0JmUuYnV0dG9ucz8xOjImZS5idXR0b25zPzI6MzticmVhaztjYXNlIm1vdXNldXAiOmk9MCxyPWUuYnV0dG9uPDM/ZS5idXR0b246MzticmVhaztjYXNlIm1vdXNlZG93biI6aT0xLHI9ZS5idXR0b248Mz9lLmJ1dHRvbjozO2JyZWFrO2Nhc2Uid2hlZWwiOjAhPT1lLmRlbHRhWSYmKGk9ZS5kZWx0YVk8MD8wOjEpLHI9NDticmVhaztkZWZhdWx0OnJldHVybiExfXJldHVybiEodm9pZCAwPT09aXx8dm9pZCAwPT09cnx8cj40KSYmdC5jb3JlTW91c2VTZXJ2aWNlLnRyaWdnZXJNb3VzZUV2ZW50KHtjb2w6bi54LTMzLHJvdzpuLnktMzMsYnV0dG9uOnIsYWN0aW9uOmksY3RybDplLmN0cmxLZXksYWx0OmUuYWx0S2V5LHNoaWZ0OmUuc2hpZnRLZXl9KX12YXIgbj17bW91c2V1cDpudWxsLHdoZWVsOm51bGwsbW91c2VkcmFnOm51bGwsbW91c2Vtb3ZlOm51bGx9LG89ZnVuY3Rpb24odCl7cmV0dXJuIGkodCksdC5idXR0b25zfHwoZS5fZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2V1cCIsbi5tb3VzZXVwKSxuLm1vdXNlZHJhZyYmZS5fZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2Vtb3ZlIixuLm1vdXNlZHJhZykpLGUuY2FuY2VsKHQpfSxzPWZ1bmN0aW9uKHQpe3JldHVybiBpKHQpLGUuY2FuY2VsKHQsITApfSxhPWZ1bmN0aW9uKGUpe2UuYnV0dG9ucyYmaShlKX0sbD1mdW5jdGlvbihlKXtlLmJ1dHRvbnN8fGkoZSl9O3RoaXMucmVnaXN0ZXIodGhpcy5jb3JlTW91c2VTZXJ2aWNlLm9uUHJvdG9jb2xDaGFuZ2UoKGZ1bmN0aW9uKHQpe3Q/KCJkZWJ1ZyI9PT1lLm9wdGlvbnNTZXJ2aWNlLm9wdGlvbnMubG9nTGV2ZWwmJmUuX2xvZ1NlcnZpY2UuZGVidWcoIkJpbmRpbmcgdG8gbW91c2UgZXZlbnRzOiIsZS5jb3JlTW91c2VTZXJ2aWNlLmV4cGxhaW5FdmVudHModCkpLGUuZWxlbWVudC5jbGFzc0xpc3QuYWRkKCJlbmFibGUtbW91c2UtZXZlbnRzIiksZS5fc2VsZWN0aW9uU2VydmljZS5kaXNhYmxlKCkpOihlLl9sb2dTZXJ2aWNlLmRlYnVnKCJVbmJpbmRpbmcgZnJvbSBtb3VzZSBldmVudHMuIiksZS5lbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoImVuYWJsZS1tb3VzZS1ldmVudHMiKSxlLl9zZWxlY3Rpb25TZXJ2aWNlLmVuYWJsZSgpKSw4JnQ/bi5tb3VzZW1vdmV8fChyLmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNlbW92ZSIsbCksbi5tb3VzZW1vdmU9bCk6KHIucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2Vtb3ZlIixuLm1vdXNlbW92ZSksbi5tb3VzZW1vdmU9bnVsbCksMTYmdD9uLndoZWVsfHwoci5hZGRFdmVudExpc3RlbmVyKCJ3aGVlbCIscyx7cGFzc2l2ZTohMX0pLG4ud2hlZWw9cyk6KHIucmVtb3ZlRXZlbnRMaXN0ZW5lcigid2hlZWwiLG4ud2hlZWwpLG4ud2hlZWw9bnVsbCksMiZ0P24ubW91c2V1cHx8KG4ubW91c2V1cD1vKTooZS5fZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2V1cCIsbi5tb3VzZXVwKSxuLm1vdXNldXA9bnVsbCksNCZ0P24ubW91c2VkcmFnfHwobi5tb3VzZWRyYWc9YSk6KGUuX2RvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoIm1vdXNlbW92ZSIsbi5tb3VzZWRyYWcpLG4ubW91c2VkcmFnPW51bGwpfSkpKSx0aGlzLmNvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2w9dGhpcy5jb3JlTW91c2VTZXJ2aWNlLmFjdGl2ZVByb3RvY29sLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHIsIm1vdXNlZG93biIsKGZ1bmN0aW9uKHQpe2lmKHQucHJldmVudERlZmF1bHQoKSxlLmZvY3VzKCksZS5jb3JlTW91c2VTZXJ2aWNlLmFyZU1vdXNlRXZlbnRzQWN0aXZlJiYhZS5fc2VsZWN0aW9uU2VydmljZS5zaG91bGRGb3JjZVNlbGVjdGlvbih0KSlyZXR1cm4gaSh0KSxuLm1vdXNldXAmJmUuX2RvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNldXAiLG4ubW91c2V1cCksbi5tb3VzZWRyYWcmJmUuX2RvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNlbW92ZSIsbi5tb3VzZWRyYWcpLGUuY2FuY2VsKHQpfSkpKSx0aGlzLnJlZ2lzdGVyKCgwLGQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKShyLCJ3aGVlbCIsKGZ1bmN0aW9uKHQpe2lmKCFuLndoZWVsKXtpZighZS5idWZmZXIuaGFzU2Nyb2xsYmFjayl7dmFyIHI9ZS52aWV3cG9ydC5nZXRMaW5lc1Njcm9sbGVkKHQpO2lmKDA9PT1yKXJldHVybjtmb3IodmFyIGk9Yy5DMC5FU0MrKGUuY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmFwcGxpY2F0aW9uQ3Vyc29yS2V5cz8iTyI6IlsiKSsodC5kZWx0YVk8MD8iQSI6IkIiKSxvPSIiLHM9MDtzPE1hdGguYWJzKHIpO3MrKylvKz1pO3JldHVybiBlLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQobywhMCksZS5jYW5jZWwodCwhMCl9cmV0dXJuIGUudmlld3BvcnQub25XaGVlbCh0KT9lLmNhbmNlbCh0KTp2b2lkIDB9fSkse3Bhc3NpdmU6ITF9KSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikociwidG91Y2hzdGFydCIsKGZ1bmN0aW9uKHQpe2lmKCFlLmNvcmVNb3VzZVNlcnZpY2UuYXJlTW91c2VFdmVudHNBY3RpdmUpcmV0dXJuIGUudmlld3BvcnQub25Ub3VjaFN0YXJ0KHQpLGUuY2FuY2VsKHQpfSkse3Bhc3NpdmU6ITB9KSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikociwidG91Y2htb3ZlIiwoZnVuY3Rpb24odCl7aWYoIWUuY29yZU1vdXNlU2VydmljZS5hcmVNb3VzZUV2ZW50c0FjdGl2ZSlyZXR1cm4gZS52aWV3cG9ydC5vblRvdWNoTW92ZSh0KT92b2lkIDA6ZS5jYW5jZWwodCl9KSx7cGFzc2l2ZTohMX0pKX0sdC5wcm90b3R5cGUucmVmcmVzaD1mdW5jdGlvbihlLHQpe3ZhciByO251bGw9PT0ocj10aGlzLl9yZW5kZXJTZXJ2aWNlKXx8dm9pZCAwPT09cnx8ci5yZWZyZXNoUm93cyhlLHQpfSx0LnByb3RvdHlwZS5fcXVldWVMaW5raWZpY2F0aW9uPWZ1bmN0aW9uKGUsdCl7dmFyIHI7bnVsbD09PShyPXRoaXMubGlua2lmaWVyKXx8dm9pZCAwPT09cnx8ci5saW5raWZ5Um93cyhlLHQpfSx0LnByb3RvdHlwZS51cGRhdGVDdXJzb3JTdHlsZT1mdW5jdGlvbihlKXt2YXIgdDsobnVsbD09PSh0PXRoaXMuX3NlbGVjdGlvblNlcnZpY2UpfHx2b2lkIDA9PT10P3ZvaWQgMDp0LnNob3VsZENvbHVtblNlbGVjdChlKSk/dGhpcy5lbGVtZW50LmNsYXNzTGlzdC5hZGQoImNvbHVtbi1zZWxlY3QiKTp0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgiY29sdW1uLXNlbGVjdCIpfSx0LnByb3RvdHlwZS5fc2hvd0N1cnNvcj1mdW5jdGlvbigpe3RoaXMuY29yZVNlcnZpY2UuaXNDdXJzb3JJbml0aWFsaXplZHx8KHRoaXMuY29yZVNlcnZpY2UuaXNDdXJzb3JJbml0aWFsaXplZD0hMCx0aGlzLnJlZnJlc2godGhpcy5idWZmZXIueSx0aGlzLmJ1ZmZlci55KSl9LHQucHJvdG90eXBlLnNjcm9sbExpbmVzPWZ1bmN0aW9uKHQscixpKXt2b2lkIDA9PT1pJiYoaT0wKSxlLnByb3RvdHlwZS5zY3JvbGxMaW5lcy5jYWxsKHRoaXMsdCxyLGkpLHRoaXMucmVmcmVzaCgwLHRoaXMucm93cy0xKX0sdC5wcm90b3R5cGUucGFzdGU9ZnVuY3Rpb24oZSl7KDAsYS5wYXN0ZSkoZSx0aGlzLnRleHRhcmVhLHRoaXMuY29yZVNlcnZpY2UpfSx0LnByb3RvdHlwZS5hdHRhY2hDdXN0b21LZXlFdmVudEhhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5fY3VzdG9tS2V5RXZlbnRIYW5kbGVyPWV9LHQucHJvdG90eXBlLnJlZ2lzdGVyTGlua01hdGNoZXI9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMubGlua2lmaWVyLnJlZ2lzdGVyTGlua01hdGNoZXIoZSx0LHIpO3JldHVybiB0aGlzLnJlZnJlc2goMCx0aGlzLnJvd3MtMSksaX0sdC5wcm90b3R5cGUuZGVyZWdpc3RlckxpbmtNYXRjaGVyPWZ1bmN0aW9uKGUpe3RoaXMubGlua2lmaWVyLmRlcmVnaXN0ZXJMaW5rTWF0Y2hlcihlKSYmdGhpcy5yZWZyZXNoKDAsdGhpcy5yb3dzLTEpfSx0LnByb3RvdHlwZS5yZWdpc3RlckxpbmtQcm92aWRlcj1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5saW5raWZpZXIyLnJlZ2lzdGVyTGlua1Byb3ZpZGVyKGUpfSx0LnByb3RvdHlwZS5yZWdpc3RlckNoYXJhY3RlckpvaW5lcj1mdW5jdGlvbihlKXtpZighdGhpcy5fY2hhcmFjdGVySm9pbmVyU2VydmljZSl0aHJvdyBuZXcgRXJyb3IoIlRlcm1pbmFsIG11c3QgYmUgb3BlbmVkIGZpcnN0Iik7dmFyIHQ9dGhpcy5fY2hhcmFjdGVySm9pbmVyU2VydmljZS5yZWdpc3RlcihlKTtyZXR1cm4gdGhpcy5yZWZyZXNoKDAsdGhpcy5yb3dzLTEpLHR9LHQucHJvdG90eXBlLmRlcmVnaXN0ZXJDaGFyYWN0ZXJKb2luZXI9ZnVuY3Rpb24oZSl7aWYoIXRoaXMuX2NoYXJhY3RlckpvaW5lclNlcnZpY2UpdGhyb3cgbmV3IEVycm9yKCJUZXJtaW5hbCBtdXN0IGJlIG9wZW5lZCBmaXJzdCIpO3RoaXMuX2NoYXJhY3RlckpvaW5lclNlcnZpY2UuZGVyZWdpc3RlcihlKSYmdGhpcy5yZWZyZXNoKDAsdGhpcy5yb3dzLTEpfSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm1hcmtlcnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5idWZmZXIubWFya2Vyc30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSx0LnByb3RvdHlwZS5hZGRNYXJrZXI9ZnVuY3Rpb24oZSl7aWYodGhpcy5idWZmZXI9PT10aGlzLmJ1ZmZlcnMubm9ybWFsKXJldHVybiB0aGlzLmJ1ZmZlci5hZGRNYXJrZXIodGhpcy5idWZmZXIueWJhc2UrdGhpcy5idWZmZXIueStlKX0sdC5wcm90b3R5cGUuaGFzU2VsZWN0aW9uPWZ1bmN0aW9uKCl7cmV0dXJuISF0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlJiZ0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLmhhc1NlbGVjdGlvbn0sdC5wcm90b3R5cGUuc2VsZWN0PWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLnNldFNlbGVjdGlvbihlLHQscil9LHQucHJvdG90eXBlLmdldFNlbGVjdGlvbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlP3RoaXMuX3NlbGVjdGlvblNlcnZpY2Uuc2VsZWN0aW9uVGV4dDoiIn0sdC5wcm90b3R5cGUuZ2V0U2VsZWN0aW9uUG9zaXRpb249ZnVuY3Rpb24oKXtpZih0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlJiZ0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLmhhc1NlbGVjdGlvbilyZXR1cm57c3RhcnRDb2x1bW46dGhpcy5fc2VsZWN0aW9uU2VydmljZS5zZWxlY3Rpb25TdGFydFswXSxzdGFydFJvdzp0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLnNlbGVjdGlvblN0YXJ0WzFdLGVuZENvbHVtbjp0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLnNlbGVjdGlvbkVuZFswXSxlbmRSb3c6dGhpcy5fc2VsZWN0aW9uU2VydmljZS5zZWxlY3Rpb25FbmRbMV19fSx0LnByb3RvdHlwZS5jbGVhclNlbGVjdGlvbj1mdW5jdGlvbigpe3ZhciBlO251bGw9PT0oZT10aGlzLl9zZWxlY3Rpb25TZXJ2aWNlKXx8dm9pZCAwPT09ZXx8ZS5jbGVhclNlbGVjdGlvbigpfSx0LnByb3RvdHlwZS5zZWxlY3RBbGw9ZnVuY3Rpb24oKXt2YXIgZTtudWxsPT09KGU9dGhpcy5fc2VsZWN0aW9uU2VydmljZSl8fHZvaWQgMD09PWV8fGUuc2VsZWN0QWxsKCl9LHQucHJvdG90eXBlLnNlbGVjdExpbmVzPWZ1bmN0aW9uKGUsdCl7dmFyIHI7bnVsbD09PShyPXRoaXMuX3NlbGVjdGlvblNlcnZpY2UpfHx2b2lkIDA9PT1yfHxyLnNlbGVjdExpbmVzKGUsdCl9LHQucHJvdG90eXBlLl9rZXlEb3duPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2tleURvd25IYW5kbGVkPSExLHRoaXMuX2N1c3RvbUtleUV2ZW50SGFuZGxlciYmITE9PT10aGlzLl9jdXN0b21LZXlFdmVudEhhbmRsZXIoZSkpcmV0dXJuITE7aWYoIXRoaXMuX2NvbXBvc2l0aW9uSGVscGVyLmtleWRvd24oZSkpcmV0dXJuIHRoaXMuYnVmZmVyLnliYXNlIT09dGhpcy5idWZmZXIueWRpc3AmJnRoaXMuX2J1ZmZlclNlcnZpY2Uuc2Nyb2xsVG9Cb3R0b20oKSwhMTsiRGVhZCIhPT1lLmtleSYmIkFsdEdyYXBoIiE9PWUua2V5fHwodGhpcy5fdW5wcm9jZXNzZWREZWFkS2V5PSEwKTt2YXIgdD0oMCxiLmV2YWx1YXRlS2V5Ym9hcmRFdmVudCkoZSx0aGlzLmNvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5hcHBsaWNhdGlvbkN1cnNvcktleXMsdGhpcy5icm93c2VyLmlzTWFjLHRoaXMub3B0aW9ucy5tYWNPcHRpb25Jc01ldGEpO2lmKHRoaXMudXBkYXRlQ3Vyc29yU3R5bGUoZSksMz09PXQudHlwZXx8Mj09PXQudHlwZSl7dmFyIHI9dGhpcy5yb3dzLTE7cmV0dXJuIHRoaXMuc2Nyb2xsTGluZXMoMj09PXQudHlwZT8tcjpyKSx0aGlzLmNhbmNlbChlLCEwKX1yZXR1cm4gMT09PXQudHlwZSYmdGhpcy5zZWxlY3RBbGwoKSwhIXRoaXMuX2lzVGhpcmRMZXZlbFNoaWZ0KHRoaXMuYnJvd3NlcixlKXx8KHQuY2FuY2VsJiZ0aGlzLmNhbmNlbChlLCEwKSwhdC5rZXl8fCh0aGlzLl91bnByb2Nlc3NlZERlYWRLZXk/KHRoaXMuX3VucHJvY2Vzc2VkRGVhZEtleT0hMSwhMCk6KHQua2V5IT09Yy5DMC5FVFgmJnQua2V5IT09Yy5DMC5DUnx8KHRoaXMudGV4dGFyZWEudmFsdWU9IiIpLHRoaXMuX29uS2V5LmZpcmUoe2tleTp0LmtleSxkb21FdmVudDplfSksdGhpcy5fc2hvd0N1cnNvcigpLHRoaXMuY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudCh0LmtleSwhMCksdGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcmVlblJlYWRlck1vZGU/dm9pZCh0aGlzLl9rZXlEb3duSGFuZGxlZD0hMCk6dGhpcy5jYW5jZWwoZSwhMCkpKSl9LHQucHJvdG90eXBlLl9pc1RoaXJkTGV2ZWxTaGlmdD1mdW5jdGlvbihlLHQpe3ZhciByPWUuaXNNYWMmJiF0aGlzLm9wdGlvbnMubWFjT3B0aW9uSXNNZXRhJiZ0LmFsdEtleSYmIXQuY3RybEtleSYmIXQubWV0YUtleXx8ZS5pc1dpbmRvd3MmJnQuYWx0S2V5JiZ0LmN0cmxLZXkmJiF0Lm1ldGFLZXl8fGUuaXNXaW5kb3dzJiZ0LmdldE1vZGlmaWVyU3RhdGUoIkFsdEdyYXBoIik7cmV0dXJuImtleXByZXNzIj09PXQudHlwZT9yOnImJighdC5rZXlDb2RlfHx0LmtleUNvZGU+NDcpfSx0LnByb3RvdHlwZS5fa2V5VXA9ZnVuY3Rpb24oZSl7dGhpcy5fY3VzdG9tS2V5RXZlbnRIYW5kbGVyJiYhMT09PXRoaXMuX2N1c3RvbUtleUV2ZW50SGFuZGxlcihlKXx8KGZ1bmN0aW9uKGUpe3JldHVybiAxNj09PWUua2V5Q29kZXx8MTc9PT1lLmtleUNvZGV8fDE4PT09ZS5rZXlDb2RlfShlKXx8dGhpcy5mb2N1cygpLHRoaXMudXBkYXRlQ3Vyc29yU3R5bGUoZSksdGhpcy5fa2V5UHJlc3NIYW5kbGVkPSExKX0sdC5wcm90b3R5cGUuX2tleVByZXNzPWZ1bmN0aW9uKGUpe3ZhciB0O2lmKHRoaXMuX2tleVByZXNzSGFuZGxlZD0hMSx0aGlzLl9rZXlEb3duSGFuZGxlZClyZXR1cm4hMTtpZih0aGlzLl9jdXN0b21LZXlFdmVudEhhbmRsZXImJiExPT09dGhpcy5fY3VzdG9tS2V5RXZlbnRIYW5kbGVyKGUpKXJldHVybiExO2lmKHRoaXMuY2FuY2VsKGUpLGUuY2hhckNvZGUpdD1lLmNoYXJDb2RlO2Vsc2UgaWYobnVsbD09PWUud2hpY2h8fHZvaWQgMD09PWUud2hpY2gpdD1lLmtleUNvZGU7ZWxzZXtpZigwPT09ZS53aGljaHx8MD09PWUuY2hhckNvZGUpcmV0dXJuITE7dD1lLndoaWNofXJldHVybiEoIXR8fChlLmFsdEtleXx8ZS5jdHJsS2V5fHxlLm1ldGFLZXkpJiYhdGhpcy5faXNUaGlyZExldmVsU2hpZnQodGhpcy5icm93c2VyLGUpfHwodD1TdHJpbmcuZnJvbUNoYXJDb2RlKHQpLHRoaXMuX29uS2V5LmZpcmUoe2tleTp0LGRvbUV2ZW50OmV9KSx0aGlzLl9zaG93Q3Vyc29yKCksdGhpcy5jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHQsITApLHRoaXMuX2tleVByZXNzSGFuZGxlZD0hMCx0aGlzLl91bnByb2Nlc3NlZERlYWRLZXk9ITEsMCkpfSx0LnByb3RvdHlwZS5faW5wdXRFdmVudD1mdW5jdGlvbihlKXtpZihlLmRhdGEmJiJpbnNlcnRUZXh0Ij09PWUuaW5wdXRUeXBlJiYhZS5jb21wb3NlZCYmIXRoaXMub3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5zY3JlZW5SZWFkZXJNb2RlKXtpZih0aGlzLl9rZXlQcmVzc0hhbmRsZWQpcmV0dXJuITE7dGhpcy5fdW5wcm9jZXNzZWREZWFkS2V5PSExO3ZhciB0PWUuZGF0YTtyZXR1cm4gdGhpcy5jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHQsITApLHRoaXMuY2FuY2VsKGUpLCEwfXJldHVybiExfSx0LnByb3RvdHlwZS5iZWxsPWZ1bmN0aW9uKCl7dmFyIGU7dGhpcy5fc291bmRCZWxsKCkmJihudWxsPT09KGU9dGhpcy5fc291bmRTZXJ2aWNlKXx8dm9pZCAwPT09ZXx8ZS5wbGF5QmVsbFNvdW5kKCkpLHRoaXMuX29uQmVsbC5maXJlKCl9LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbih0LHIpe3QhPT10aGlzLmNvbHN8fHIhPT10aGlzLnJvd3M/ZS5wcm90b3R5cGUucmVzaXplLmNhbGwodGhpcyx0LHIpOnRoaXMuX2NoYXJTaXplU2VydmljZSYmIXRoaXMuX2NoYXJTaXplU2VydmljZS5oYXNWYWxpZFNpemUmJnRoaXMuX2NoYXJTaXplU2VydmljZS5tZWFzdXJlKCl9LHQucHJvdG90eXBlLl9hZnRlclJlc2l6ZT1mdW5jdGlvbihlLHQpe3ZhciByLGk7bnVsbD09PShyPXRoaXMuX2NoYXJTaXplU2VydmljZSl8fHZvaWQgMD09PXJ8fHIubWVhc3VyZSgpLG51bGw9PT0oaT10aGlzLnZpZXdwb3J0KXx8dm9pZCAwPT09aXx8aS5zeW5jU2Nyb2xsQXJlYSghMCl9LHQucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7aWYoMCE9PXRoaXMuYnVmZmVyLnliYXNlfHwwIT09dGhpcy5idWZmZXIueSl7dGhpcy5idWZmZXIubGluZXMuc2V0KDAsdGhpcy5idWZmZXIubGluZXMuZ2V0KHRoaXMuYnVmZmVyLnliYXNlK3RoaXMuYnVmZmVyLnkpKSx0aGlzLmJ1ZmZlci5saW5lcy5sZW5ndGg9MSx0aGlzLmJ1ZmZlci55ZGlzcD0wLHRoaXMuYnVmZmVyLnliYXNlPTAsdGhpcy5idWZmZXIueT0wO2Zvcih2YXIgZT0xO2U8dGhpcy5yb3dzO2UrKyl0aGlzLmJ1ZmZlci5saW5lcy5wdXNoKHRoaXMuYnVmZmVyLmdldEJsYW5rTGluZShDLkRFRkFVTFRfQVRUUl9EQVRBKSk7dGhpcy5yZWZyZXNoKDAsdGhpcy5yb3dzLTEpLHRoaXMuX29uU2Nyb2xsLmZpcmUoe3Bvc2l0aW9uOnRoaXMuYnVmZmVyLnlkaXNwLHNvdXJjZTowfSl9fSx0LnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe3ZhciB0LHI7dGhpcy5vcHRpb25zLnJvd3M9dGhpcy5yb3dzLHRoaXMub3B0aW9ucy5jb2xzPXRoaXMuY29sczt2YXIgaT10aGlzLl9jdXN0b21LZXlFdmVudEhhbmRsZXI7dGhpcy5fc2V0dXAoKSxlLnByb3RvdHlwZS5yZXNldC5jYWxsKHRoaXMpLG51bGw9PT0odD10aGlzLl9zZWxlY3Rpb25TZXJ2aWNlKXx8dm9pZCAwPT09dHx8dC5yZXNldCgpLHRoaXMuX2N1c3RvbUtleUV2ZW50SGFuZGxlcj1pLHRoaXMucmVmcmVzaCgwLHRoaXMucm93cy0xKSxudWxsPT09KHI9dGhpcy52aWV3cG9ydCl8fHZvaWQgMD09PXJ8fHIuc3luY1Njcm9sbEFyZWEoKX0sdC5wcm90b3R5cGUuY2xlYXJUZXh0dXJlQXRsYXM9ZnVuY3Rpb24oKXt2YXIgZTtudWxsPT09KGU9dGhpcy5fcmVuZGVyU2VydmljZSl8fHZvaWQgMD09PWV8fGUuY2xlYXJUZXh0dXJlQXRsYXMoKX0sdC5wcm90b3R5cGUuX3JlcG9ydEZvY3VzPWZ1bmN0aW9uKCl7dmFyIGU7KG51bGw9PT0oZT10aGlzLmVsZW1lbnQpfHx2b2lkIDA9PT1lP3ZvaWQgMDplLmNsYXNzTGlzdC5jb250YWlucygiZm9jdXMiKSk/dGhpcy5jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KGMuQzAuRVNDKyJbSSIpOnRoaXMuY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChjLkMwLkVTQysiW08iKX0sdC5wcm90b3R5cGUuX3JlcG9ydFdpbmRvd3NPcHRpb25zPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX3JlbmRlclNlcnZpY2Upc3dpdGNoKGUpe2Nhc2UgbC5XaW5kb3dzT3B0aW9uc1JlcG9ydFR5cGUuR0VUX1dJTl9TSVpFX1BJWEVMUzp2YXIgdD10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuc2NhbGVkQ2FudmFzV2lkdGgudG9GaXhlZCgwKSxyPXRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5zY2FsZWRDYW52YXNIZWlnaHQudG9GaXhlZCgwKTt0aGlzLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoYy5DMC5FU0MrIls0OyIrcisiOyIrdCsidCIpO2JyZWFrO2Nhc2UgbC5XaW5kb3dzT3B0aW9uc1JlcG9ydFR5cGUuR0VUX0NFTExfU0laRV9QSVhFTFM6dmFyIGk9dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLnNjYWxlZENlbGxXaWR0aC50b0ZpeGVkKDApLG49dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLnNjYWxlZENlbGxIZWlnaHQudG9GaXhlZCgwKTt0aGlzLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoYy5DMC5FU0MrIls2OyIrbisiOyIraSsidCIpfX0sdC5wcm90b3R5cGUuY2FuY2VsPWZ1bmN0aW9uKGUsdCl7aWYodGhpcy5vcHRpb25zLmNhbmNlbEV2ZW50c3x8dClyZXR1cm4gZS5wcmV2ZW50RGVmYXVsdCgpLGUuc3RvcFByb3BhZ2F0aW9uKCksITF9LHQucHJvdG90eXBlLl92aXN1YWxCZWxsPWZ1bmN0aW9uKCl7cmV0dXJuITF9LHQucHJvdG90eXBlLl9zb3VuZEJlbGw9ZnVuY3Rpb24oKXtyZXR1cm4ic291bmQiPT09dGhpcy5vcHRpb25zLmJlbGxTdHlsZX0sdH0oUi5Db3JlVGVybWluYWwpO3QuVGVybWluYWw9UH0sOTkyNDooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlRpbWVCYXNlZERlYm91bmNlcj12b2lkIDA7dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCl7dm9pZCAwPT09dCYmKHQ9MWUzKSx0aGlzLl9yZW5kZXJDYWxsYmFjaz1lLHRoaXMuX2RlYm91bmNlVGhyZXNob2xkTVM9dCx0aGlzLl9sYXN0UmVmcmVzaE1zPTAsdGhpcy5fYWRkaXRpb25hbFJlZnJlc2hSZXF1ZXN0ZWQ9ITF9cmV0dXJuIGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt0aGlzLl9yZWZyZXNoVGltZW91dElEJiZjbGVhclRpbWVvdXQodGhpcy5fcmVmcmVzaFRpbWVvdXRJRCl9LGUucHJvdG90eXBlLnJlZnJlc2g9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXM7dGhpcy5fcm93Q291bnQ9cixlPXZvaWQgMCE9PWU/ZTowLHQ9dm9pZCAwIT09dD90OnRoaXMuX3Jvd0NvdW50LTEsdGhpcy5fcm93U3RhcnQ9dm9pZCAwIT09dGhpcy5fcm93U3RhcnQ/TWF0aC5taW4odGhpcy5fcm93U3RhcnQsZSk6ZSx0aGlzLl9yb3dFbmQ9dm9pZCAwIT09dGhpcy5fcm93RW5kP01hdGgubWF4KHRoaXMuX3Jvd0VuZCx0KTp0O3ZhciBuPURhdGUubm93KCk7aWYobi10aGlzLl9sYXN0UmVmcmVzaE1zPj10aGlzLl9kZWJvdW5jZVRocmVzaG9sZE1TKXRoaXMuX2xhc3RSZWZyZXNoTXM9bix0aGlzLl9pbm5lclJlZnJlc2goKTtlbHNlIGlmKCF0aGlzLl9hZGRpdGlvbmFsUmVmcmVzaFJlcXVlc3RlZCl7dmFyIG89bi10aGlzLl9sYXN0UmVmcmVzaE1zLHM9dGhpcy5fZGVib3VuY2VUaHJlc2hvbGRNUy1vO3RoaXMuX2FkZGl0aW9uYWxSZWZyZXNoUmVxdWVzdGVkPSEwLHRoaXMuX3JlZnJlc2hUaW1lb3V0SUQ9d2luZG93LnNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7aS5fbGFzdFJlZnJlc2hNcz1EYXRlLm5vdygpLGkuX2lubmVyUmVmcmVzaCgpLGkuX2FkZGl0aW9uYWxSZWZyZXNoUmVxdWVzdGVkPSExLGkuX3JlZnJlc2hUaW1lb3V0SUQ9dm9pZCAwfSkscyl9fSxlLnByb3RvdHlwZS5faW5uZXJSZWZyZXNoPWZ1bmN0aW9uKCl7aWYodm9pZCAwIT09dGhpcy5fcm93U3RhcnQmJnZvaWQgMCE9PXRoaXMuX3Jvd0VuZCYmdm9pZCAwIT09dGhpcy5fcm93Q291bnQpe3ZhciBlPU1hdGgubWF4KHRoaXMuX3Jvd1N0YXJ0LDApLHQ9TWF0aC5taW4odGhpcy5fcm93RW5kLHRoaXMuX3Jvd0NvdW50LTEpO3RoaXMuX3Jvd1N0YXJ0PXZvaWQgMCx0aGlzLl9yb3dFbmQ9dm9pZCAwLHRoaXMuX3JlbmRlckNhbGxiYWNrKGUsdCl9fSxlfSgpO3QuVGltZUJhc2VkRGVib3VuY2VyPXJ9LDE2ODA6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSksbz10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LHM9dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuVmlld3BvcnQ9dm9pZCAwO3ZhciBhPXIoODQ0KSxjPXIoMzY1NiksbD1yKDQ3MjUpLHU9cigyNTg1KSxoPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyLGksbixvLHMsYSxsKXt2YXIgdT1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIHUuX3Njcm9sbExpbmVzPXQsdS5fdmlld3BvcnRFbGVtZW50PXIsdS5fc2Nyb2xsQXJlYT1pLHUuX2VsZW1lbnQ9bix1Ll9idWZmZXJTZXJ2aWNlPW8sdS5fb3B0aW9uc1NlcnZpY2U9cyx1Ll9jaGFyU2l6ZVNlcnZpY2U9YSx1Ll9yZW5kZXJTZXJ2aWNlPWwsdS5zY3JvbGxCYXJXaWR0aD0wLHUuX2N1cnJlbnRSb3dIZWlnaHQ9MCx1Ll9jdXJyZW50U2NhbGVkQ2VsbEhlaWdodD0wLHUuX2xhc3RSZWNvcmRlZEJ1ZmZlckxlbmd0aD0wLHUuX2xhc3RSZWNvcmRlZFZpZXdwb3J0SGVpZ2h0PTAsdS5fbGFzdFJlY29yZGVkQnVmZmVySGVpZ2h0PTAsdS5fbGFzdFRvdWNoWT0wLHUuX2xhc3RTY3JvbGxUb3A9MCx1Ll9sYXN0SGFkU2Nyb2xsQmFyPSExLHUuX3doZWVsUGFydGlhbFNjcm9sbD0wLHUuX3JlZnJlc2hBbmltYXRpb25GcmFtZT1udWxsLHUuX2lnbm9yZU5leHRTY3JvbGxFdmVudD0hMSx1LnNjcm9sbEJhcldpZHRoPXUuX3ZpZXdwb3J0RWxlbWVudC5vZmZzZXRXaWR0aC11Ll9zY3JvbGxBcmVhLm9mZnNldFdpZHRofHwxNSx1Ll9sYXN0SGFkU2Nyb2xsQmFyPSEwLHUucmVnaXN0ZXIoKDAsYy5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHUuX3ZpZXdwb3J0RWxlbWVudCwic2Nyb2xsIix1Ll9vblNjcm9sbC5iaW5kKHUpKSksdS5fYWN0aXZlQnVmZmVyPXUuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLHUucmVnaXN0ZXIodS5fYnVmZmVyU2VydmljZS5idWZmZXJzLm9uQnVmZmVyQWN0aXZhdGUoKGZ1bmN0aW9uKGUpe3JldHVybiB1Ll9hY3RpdmVCdWZmZXI9ZS5hY3RpdmVCdWZmZXJ9KSkpLHUuX3JlbmRlckRpbWVuc2lvbnM9dS5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLHUucmVnaXN0ZXIodS5fcmVuZGVyU2VydmljZS5vbkRpbWVuc2lvbnNDaGFuZ2UoKGZ1bmN0aW9uKGUpe3JldHVybiB1Ll9yZW5kZXJEaW1lbnNpb25zPWV9KSkpLHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIHUuc3luY1Njcm9sbEFyZWEoKX0pLDApLHV9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5vblRoZW1lQ2hhbmdlPWZ1bmN0aW9uKGUpe3RoaXMuX3ZpZXdwb3J0RWxlbWVudC5zdHlsZS5iYWNrZ3JvdW5kQ29sb3I9ZS5iYWNrZ3JvdW5kLmNzc30sdC5wcm90b3R5cGUuX3JlZnJlc2g9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcztpZihlKXJldHVybiB0aGlzLl9pbm5lclJlZnJlc2goKSx2b2lkKG51bGwhPT10aGlzLl9yZWZyZXNoQW5pbWF0aW9uRnJhbWUmJmNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMuX3JlZnJlc2hBbmltYXRpb25GcmFtZSkpO251bGw9PT10aGlzLl9yZWZyZXNoQW5pbWF0aW9uRnJhbWUmJih0aGlzLl9yZWZyZXNoQW5pbWF0aW9uRnJhbWU9cmVxdWVzdEFuaW1hdGlvbkZyYW1lKChmdW5jdGlvbigpe3JldHVybiB0Ll9pbm5lclJlZnJlc2goKX0pKSl9LHQucHJvdG90eXBlLl9pbm5lclJlZnJlc2g9ZnVuY3Rpb24oKXtpZih0aGlzLl9jaGFyU2l6ZVNlcnZpY2UuaGVpZ2h0PjApe3RoaXMuX2N1cnJlbnRSb3dIZWlnaHQ9dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLnNjYWxlZENlbGxIZWlnaHQvd2luZG93LmRldmljZVBpeGVsUmF0aW8sdGhpcy5fY3VycmVudFNjYWxlZENlbGxIZWlnaHQ9dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLnNjYWxlZENlbGxIZWlnaHQsdGhpcy5fbGFzdFJlY29yZGVkVmlld3BvcnRIZWlnaHQ9dGhpcy5fdmlld3BvcnRFbGVtZW50Lm9mZnNldEhlaWdodDt2YXIgZT1NYXRoLnJvdW5kKHRoaXMuX2N1cnJlbnRSb3dIZWlnaHQqdGhpcy5fbGFzdFJlY29yZGVkQnVmZmVyTGVuZ3RoKSsodGhpcy5fbGFzdFJlY29yZGVkVmlld3BvcnRIZWlnaHQtdGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLmNhbnZhc0hlaWdodCk7dGhpcy5fbGFzdFJlY29yZGVkQnVmZmVySGVpZ2h0IT09ZSYmKHRoaXMuX2xhc3RSZWNvcmRlZEJ1ZmZlckhlaWdodD1lLHRoaXMuX3Njcm9sbEFyZWEuc3R5bGUuaGVpZ2h0PXRoaXMuX2xhc3RSZWNvcmRlZEJ1ZmZlckhlaWdodCsicHgiKX12YXIgdD10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCp0aGlzLl9jdXJyZW50Um93SGVpZ2h0O3RoaXMuX3ZpZXdwb3J0RWxlbWVudC5zY3JvbGxUb3AhPT10JiYodGhpcy5faWdub3JlTmV4dFNjcm9sbEV2ZW50PSEwLHRoaXMuX3ZpZXdwb3J0RWxlbWVudC5zY3JvbGxUb3A9dCksMD09PXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuc2Nyb2xsYmFjaz90aGlzLnNjcm9sbEJhcldpZHRoPTA6dGhpcy5zY3JvbGxCYXJXaWR0aD10aGlzLl92aWV3cG9ydEVsZW1lbnQub2Zmc2V0V2lkdGgtdGhpcy5fc2Nyb2xsQXJlYS5vZmZzZXRXaWR0aHx8MTUsdGhpcy5fbGFzdEhhZFNjcm9sbEJhcj10aGlzLnNjcm9sbEJhcldpZHRoPjA7dmFyIHI9d2luZG93LmdldENvbXB1dGVkU3R5bGUodGhpcy5fZWxlbWVudCksaT1wYXJzZUludChyLnBhZGRpbmdMZWZ0KStwYXJzZUludChyLnBhZGRpbmdSaWdodCk7dGhpcy5fdmlld3BvcnRFbGVtZW50LnN0eWxlLndpZHRoPSh0aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoKnRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyt0aGlzLnNjcm9sbEJhcldpZHRoKyh0aGlzLl9sYXN0SGFkU2Nyb2xsQmFyP2k6MCkpLnRvU3RyaW5nKCkrInB4Iix0aGlzLl9yZWZyZXNoQW5pbWF0aW9uRnJhbWU9bnVsbH0sdC5wcm90b3R5cGUuc3luY1Njcm9sbEFyZWE9ZnVuY3Rpb24oZSl7aWYodm9pZCAwPT09ZSYmKGU9ITEpLHRoaXMuX2xhc3RSZWNvcmRlZEJ1ZmZlckxlbmd0aCE9PXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLmxpbmVzLmxlbmd0aClyZXR1cm4gdGhpcy5fbGFzdFJlY29yZGVkQnVmZmVyTGVuZ3RoPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLmxpbmVzLmxlbmd0aCx2b2lkIHRoaXMuX3JlZnJlc2goZSk7dGhpcy5fbGFzdFJlY29yZGVkVmlld3BvcnRIZWlnaHQ9PT10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0JiZ0aGlzLl9sYXN0U2Nyb2xsVG9wPT09dGhpcy5fYWN0aXZlQnVmZmVyLnlkaXNwKnRoaXMuX2N1cnJlbnRSb3dIZWlnaHQmJnRoaXMuX3JlbmRlckRpbWVuc2lvbnMuc2NhbGVkQ2VsbEhlaWdodD09PXRoaXMuX2N1cnJlbnRTY2FsZWRDZWxsSGVpZ2h0P3RoaXMuX2xhc3RIYWRTY3JvbGxCYXIhPT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcm9sbGJhY2s+MCYmdGhpcy5fcmVmcmVzaChlKTp0aGlzLl9yZWZyZXNoKGUpfSx0LnByb3RvdHlwZS5fb25TY3JvbGw9ZnVuY3Rpb24oZSl7aWYodGhpcy5fbGFzdFNjcm9sbFRvcD10aGlzLl92aWV3cG9ydEVsZW1lbnQuc2Nyb2xsVG9wLHRoaXMuX3ZpZXdwb3J0RWxlbWVudC5vZmZzZXRQYXJlbnQpe2lmKHRoaXMuX2lnbm9yZU5leHRTY3JvbGxFdmVudClyZXR1cm4gdGhpcy5faWdub3JlTmV4dFNjcm9sbEV2ZW50PSExLHZvaWQgdGhpcy5fc2Nyb2xsTGluZXMoMCk7dmFyIHQ9TWF0aC5yb3VuZCh0aGlzLl9sYXN0U2Nyb2xsVG9wL3RoaXMuX2N1cnJlbnRSb3dIZWlnaHQpLXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwO3RoaXMuX3Njcm9sbExpbmVzKHQpfX0sdC5wcm90b3R5cGUuX2J1YmJsZVNjcm9sbD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX3ZpZXdwb3J0RWxlbWVudC5zY3JvbGxUb3ArdGhpcy5fbGFzdFJlY29yZGVkVmlld3BvcnRIZWlnaHQ7cmV0dXJuISh0PDAmJjAhPT10aGlzLl92aWV3cG9ydEVsZW1lbnQuc2Nyb2xsVG9wfHx0PjAmJnI8dGhpcy5fbGFzdFJlY29yZGVkQnVmZmVySGVpZ2h0KXx8KGUuY2FuY2VsYWJsZSYmZS5wcmV2ZW50RGVmYXVsdCgpLCExKX0sdC5wcm90b3R5cGUub25XaGVlbD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9nZXRQaXhlbHNTY3JvbGxlZChlKTtyZXR1cm4gMCE9PXQmJih0aGlzLl92aWV3cG9ydEVsZW1lbnQuc2Nyb2xsVG9wKz10LHRoaXMuX2J1YmJsZVNjcm9sbChlLHQpKX0sdC5wcm90b3R5cGUuX2dldFBpeGVsc1Njcm9sbGVkPWZ1bmN0aW9uKGUpe2lmKDA9PT1lLmRlbHRhWXx8ZS5zaGlmdEtleSlyZXR1cm4gMDt2YXIgdD10aGlzLl9hcHBseVNjcm9sbE1vZGlmaWVyKGUuZGVsdGFZLGUpO3JldHVybiBlLmRlbHRhTW9kZT09PVdoZWVsRXZlbnQuRE9NX0RFTFRBX0xJTkU/dCo9dGhpcy5fY3VycmVudFJvd0hlaWdodDplLmRlbHRhTW9kZT09PVdoZWVsRXZlbnQuRE9NX0RFTFRBX1BBR0UmJih0Kj10aGlzLl9jdXJyZW50Um93SGVpZ2h0KnRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyksdH0sdC5wcm90b3R5cGUuZ2V0TGluZXNTY3JvbGxlZD1mdW5jdGlvbihlKXtpZigwPT09ZS5kZWx0YVl8fGUuc2hpZnRLZXkpcmV0dXJuIDA7dmFyIHQ9dGhpcy5fYXBwbHlTY3JvbGxNb2RpZmllcihlLmRlbHRhWSxlKTtyZXR1cm4gZS5kZWx0YU1vZGU9PT1XaGVlbEV2ZW50LkRPTV9ERUxUQV9QSVhFTD8odC89dGhpcy5fY3VycmVudFJvd0hlaWdodCswLHRoaXMuX3doZWVsUGFydGlhbFNjcm9sbCs9dCx0PU1hdGguZmxvb3IoTWF0aC5hYnModGhpcy5fd2hlZWxQYXJ0aWFsU2Nyb2xsKSkqKHRoaXMuX3doZWVsUGFydGlhbFNjcm9sbD4wPzE6LTEpLHRoaXMuX3doZWVsUGFydGlhbFNjcm9sbCU9MSk6ZS5kZWx0YU1vZGU9PT1XaGVlbEV2ZW50LkRPTV9ERUxUQV9QQUdFJiYodCo9dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKSx0fSx0LnByb3RvdHlwZS5fYXBwbHlTY3JvbGxNb2RpZmllcj1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZmFzdFNjcm9sbE1vZGlmaWVyO3JldHVybiJhbHQiPT09ciYmdC5hbHRLZXl8fCJjdHJsIj09PXImJnQuY3RybEtleXx8InNoaWZ0Ij09PXImJnQuc2hpZnRLZXk/ZSp0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZhc3RTY3JvbGxTZW5zaXRpdml0eSp0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcm9sbFNlbnNpdGl2aXR5OmUqdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5zY3JvbGxTZW5zaXRpdml0eX0sdC5wcm90b3R5cGUub25Ub3VjaFN0YXJ0PWZ1bmN0aW9uKGUpe3RoaXMuX2xhc3RUb3VjaFk9ZS50b3VjaGVzWzBdLnBhZ2VZfSx0LnByb3RvdHlwZS5vblRvdWNoTW92ZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9sYXN0VG91Y2hZLWUudG91Y2hlc1swXS5wYWdlWTtyZXR1cm4gdGhpcy5fbGFzdFRvdWNoWT1lLnRvdWNoZXNbMF0ucGFnZVksMCE9PXQmJih0aGlzLl92aWV3cG9ydEVsZW1lbnQuc2Nyb2xsVG9wKz10LHRoaXMuX2J1YmJsZVNjcm9sbChlLHQpKX0sbyhbcyg0LHUuSUJ1ZmZlclNlcnZpY2UpLHMoNSx1LklPcHRpb25zU2VydmljZSkscyg2LGwuSUNoYXJTaXplU2VydmljZSkscyg3LGwuSVJlbmRlclNlcnZpY2UpXSx0KX0oYS5EaXNwb3NhYmxlKTt0LlZpZXdwb3J0PWh9LDI5NTA6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Db21wb3NpdGlvbkhlbHBlcj12b2lkIDA7dmFyIG89cig0NzI1KSxzPXIoMjU4NSksYT1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSx0LHIsaSxuLG8pe3RoaXMuX3RleHRhcmVhPWUsdGhpcy5fY29tcG9zaXRpb25WaWV3PXQsdGhpcy5fYnVmZmVyU2VydmljZT1yLHRoaXMuX29wdGlvbnNTZXJ2aWNlPWksdGhpcy5fY29yZVNlcnZpY2U9bix0aGlzLl9yZW5kZXJTZXJ2aWNlPW8sdGhpcy5faXNDb21wb3Npbmc9ITEsdGhpcy5faXNTZW5kaW5nQ29tcG9zaXRpb249ITEsdGhpcy5fY29tcG9zaXRpb25Qb3NpdGlvbj17c3RhcnQ6MCxlbmQ6MH0sdGhpcy5fZGF0YUFscmVhZHlTZW50PSIifXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImlzQ29tcG9zaW5nIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2lzQ29tcG9zaW5nfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmNvbXBvc2l0aW9uc3RhcnQ9ZnVuY3Rpb24oKXt0aGlzLl9pc0NvbXBvc2luZz0hMCx0aGlzLl9jb21wb3NpdGlvblBvc2l0aW9uLnN0YXJ0PXRoaXMuX3RleHRhcmVhLnZhbHVlLmxlbmd0aCx0aGlzLl9jb21wb3NpdGlvblZpZXcudGV4dENvbnRlbnQ9IiIsdGhpcy5fZGF0YUFscmVhZHlTZW50PSIiLHRoaXMuX2NvbXBvc2l0aW9uVmlldy5jbGFzc0xpc3QuYWRkKCJhY3RpdmUiKX0sZS5wcm90b3R5cGUuY29tcG9zaXRpb251cGRhdGU9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpczt0aGlzLl9jb21wb3NpdGlvblZpZXcudGV4dENvbnRlbnQ9ZS5kYXRhLHRoaXMudXBkYXRlQ29tcG9zaXRpb25FbGVtZW50cygpLHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7dC5fY29tcG9zaXRpb25Qb3NpdGlvbi5lbmQ9dC5fdGV4dGFyZWEudmFsdWUubGVuZ3RofSksMCl9LGUucHJvdG90eXBlLmNvbXBvc2l0aW9uZW5kPWZ1bmN0aW9uKCl7dGhpcy5fZmluYWxpemVDb21wb3NpdGlvbighMCl9LGUucHJvdG90eXBlLmtleWRvd249ZnVuY3Rpb24oZSl7aWYodGhpcy5faXNDb21wb3Npbmd8fHRoaXMuX2lzU2VuZGluZ0NvbXBvc2l0aW9uKXtpZigyMjk9PT1lLmtleUNvZGUpcmV0dXJuITE7aWYoMTY9PT1lLmtleUNvZGV8fDE3PT09ZS5rZXlDb2RlfHwxOD09PWUua2V5Q29kZSlyZXR1cm4hMTt0aGlzLl9maW5hbGl6ZUNvbXBvc2l0aW9uKCExKX1yZXR1cm4gMjI5IT09ZS5rZXlDb2RlfHwodGhpcy5faGFuZGxlQW55VGV4dGFyZWFDaGFuZ2VzKCksITEpfSxlLnByb3RvdHlwZS5fZmluYWxpemVDb21wb3NpdGlvbj1mdW5jdGlvbihlKXt2YXIgdD10aGlzO2lmKHRoaXMuX2NvbXBvc2l0aW9uVmlldy5jbGFzc0xpc3QucmVtb3ZlKCJhY3RpdmUiKSx0aGlzLl9pc0NvbXBvc2luZz0hMSxlKXt2YXIgcj17c3RhcnQ6dGhpcy5fY29tcG9zaXRpb25Qb3NpdGlvbi5zdGFydCxlbmQ6dGhpcy5fY29tcG9zaXRpb25Qb3NpdGlvbi5lbmR9O3RoaXMuX2lzU2VuZGluZ0NvbXBvc2l0aW9uPSEwLHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7dmFyIGU7dC5faXNTZW5kaW5nQ29tcG9zaXRpb24mJih0Ll9pc1NlbmRpbmdDb21wb3NpdGlvbj0hMSxyLnN0YXJ0Kz10Ll9kYXRhQWxyZWFkeVNlbnQubGVuZ3RoLChlPXQuX2lzQ29tcG9zaW5nP3QuX3RleHRhcmVhLnZhbHVlLnN1YnN0cmluZyhyLnN0YXJ0LHIuZW5kKTp0Ll90ZXh0YXJlYS52YWx1ZS5zdWJzdHJpbmcoci5zdGFydCkpLmxlbmd0aD4wJiZ0Ll9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KGUsITApKX0pLDApfWVsc2V7dGhpcy5faXNTZW5kaW5nQ29tcG9zaXRpb249ITE7dmFyIGk9dGhpcy5fdGV4dGFyZWEudmFsdWUuc3Vic3RyaW5nKHRoaXMuX2NvbXBvc2l0aW9uUG9zaXRpb24uc3RhcnQsdGhpcy5fY29tcG9zaXRpb25Qb3NpdGlvbi5lbmQpO3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoaSwhMCl9fSxlLnByb3RvdHlwZS5faGFuZGxlQW55VGV4dGFyZWFDaGFuZ2VzPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcyx0PXRoaXMuX3RleHRhcmVhLnZhbHVlO3NldFRpbWVvdXQoKGZ1bmN0aW9uKCl7aWYoIWUuX2lzQ29tcG9zaW5nKXt2YXIgcj1lLl90ZXh0YXJlYS52YWx1ZS5yZXBsYWNlKHQsIiIpO3IubGVuZ3RoPjAmJihlLl9kYXRhQWxyZWFkeVNlbnQ9cixlLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHIsITApKX19KSwwKX0sZS5wcm90b3R5cGUudXBkYXRlQ29tcG9zaXRpb25FbGVtZW50cz1mdW5jdGlvbihlKXt2YXIgdD10aGlzO2lmKHRoaXMuX2lzQ29tcG9zaW5nKXtpZih0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci5pc0N1cnNvckluVmlld3BvcnQpe3ZhciByPU1hdGgubWluKHRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLngsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLTEpLGk9dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLmFjdHVhbENlbGxIZWlnaHQsbj10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55KnRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0LG89cip0aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoO3RoaXMuX2NvbXBvc2l0aW9uVmlldy5zdHlsZS5sZWZ0PW8rInB4Iix0aGlzLl9jb21wb3NpdGlvblZpZXcuc3R5bGUudG9wPW4rInB4Iix0aGlzLl9jb21wb3NpdGlvblZpZXcuc3R5bGUuaGVpZ2h0PWkrInB4Iix0aGlzLl9jb21wb3NpdGlvblZpZXcuc3R5bGUubGluZUhlaWdodD1pKyJweCIsdGhpcy5fY29tcG9zaXRpb25WaWV3LnN0eWxlLmZvbnRGYW1pbHk9dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5mb250RmFtaWx5LHRoaXMuX2NvbXBvc2l0aW9uVmlldy5zdHlsZS5mb250U2l6ZT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZvbnRTaXplKyJweCI7dmFyIHM9dGhpcy5fY29tcG9zaXRpb25WaWV3LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO3RoaXMuX3RleHRhcmVhLnN0eWxlLmxlZnQ9bysicHgiLHRoaXMuX3RleHRhcmVhLnN0eWxlLnRvcD1uKyJweCIsdGhpcy5fdGV4dGFyZWEuc3R5bGUud2lkdGg9TWF0aC5tYXgocy53aWR0aCwxKSsicHgiLHRoaXMuX3RleHRhcmVhLnN0eWxlLmhlaWdodD1NYXRoLm1heChzLmhlaWdodCwxKSsicHgiLHRoaXMuX3RleHRhcmVhLnN0eWxlLmxpbmVIZWlnaHQ9cy5oZWlnaHQrInB4In1lfHxzZXRUaW1lb3V0KChmdW5jdGlvbigpe3JldHVybiB0LnVwZGF0ZUNvbXBvc2l0aW9uRWxlbWVudHMoITApfSksMCl9fSxpKFtuKDIscy5JQnVmZmVyU2VydmljZSksbigzLHMuSU9wdGlvbnNTZXJ2aWNlKSxuKDQscy5JQ29yZVNlcnZpY2UpLG4oNSxvLklSZW5kZXJTZXJ2aWNlKV0sZSl9KCk7dC5Db21wb3NpdGlvbkhlbHBlcj1hfSw5ODA2OihlLHQpPT57ZnVuY3Rpb24gcihlLHQpe3ZhciByPXQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7cmV0dXJuW2UuY2xpZW50WC1yLmxlZnQsZS5jbGllbnRZLXIudG9wXX1PYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5nZXRSYXdCeXRlQ29vcmRzPXQuZ2V0Q29vcmRzPXQuZ2V0Q29vcmRzUmVsYXRpdmVUb0VsZW1lbnQ9dm9pZCAwLHQuZ2V0Q29vcmRzUmVsYXRpdmVUb0VsZW1lbnQ9cix0LmdldENvb3Jkcz1mdW5jdGlvbihlLHQsaSxuLG8scyxhLGMpe2lmKG8pe3ZhciBsPXIoZSx0KTtpZihsKXJldHVybiBsWzBdPU1hdGguY2VpbCgobFswXSsoYz9zLzI6MCkpL3MpLGxbMV09TWF0aC5jZWlsKGxbMV0vYSksbFswXT1NYXRoLm1pbihNYXRoLm1heChsWzBdLDEpLGkrKGM/MTowKSksbFsxXT1NYXRoLm1pbihNYXRoLm1heChsWzFdLDEpLG4pLGx9fSx0LmdldFJhd0J5dGVDb29yZHM9ZnVuY3Rpb24oZSl7aWYoZSlyZXR1cm57eDplWzBdKzMyLHk6ZVsxXSszMn19fSw5NTA0OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5tb3ZlVG9DZWxsU2VxdWVuY2U9dm9pZCAwO3ZhciBpPXIoMjU4NCk7ZnVuY3Rpb24gbihlLHQscixpKXt2YXIgbj1lLW8ocixlKSxhPXQtbyhyLHQpLHU9TWF0aC5hYnMobi1hKS1mdW5jdGlvbihlLHQscil7Zm9yKHZhciBpPTAsbj1lLW8ocixlKSxhPXQtbyhyLHQpLGM9MDtjPE1hdGguYWJzKG4tYSk7YysrKXt2YXIgbD0iQSI9PT1zKGUsdCk/LTE6MSx1PXIuYnVmZmVyLmxpbmVzLmdldChuK2wqYyk7KG51bGw9PXU/dm9pZCAwOnUuaXNXcmFwcGVkKSYmaSsrfXJldHVybiBpfShlLHQscik7cmV0dXJuIGwodSxjKHMoZSx0KSxpKSl9ZnVuY3Rpb24gbyhlLHQpe2Zvcih2YXIgcj0wLGk9ZS5idWZmZXIubGluZXMuZ2V0KHQpLG49bnVsbD09aT92b2lkIDA6aS5pc1dyYXBwZWQ7biYmdD49MCYmdDxlLnJvd3M7KXIrKyxuPW51bGw9PShpPWUuYnVmZmVyLmxpbmVzLmdldCgtLXQpKT92b2lkIDA6aS5pc1dyYXBwZWQ7cmV0dXJuIHJ9ZnVuY3Rpb24gcyhlLHQpe3JldHVybiBlPnQ/IkEiOiJCIn1mdW5jdGlvbiBhKGUsdCxyLGksbixvKXtmb3IodmFyIHM9ZSxhPXQsYz0iIjtzIT09cnx8YSE9PWk7KXMrPW4/MTotMSxuJiZzPm8uY29scy0xPyhjKz1vLmJ1ZmZlci50cmFuc2xhdGVCdWZmZXJMaW5lVG9TdHJpbmcoYSwhMSxlLHMpLHM9MCxlPTAsYSsrKTohbiYmczwwJiYoYys9by5idWZmZXIudHJhbnNsYXRlQnVmZmVyTGluZVRvU3RyaW5nKGEsITEsMCxlKzEpLGU9cz1vLmNvbHMtMSxhLS0pO3JldHVybiBjK28uYnVmZmVyLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhhLCExLGUscyl9ZnVuY3Rpb24gYyhlLHQpe3ZhciByPXQ/Ik8iOiJbIjtyZXR1cm4gaS5DMC5FU0MrcitlfWZ1bmN0aW9uIGwoZSx0KXtlPU1hdGguZmxvb3IoZSk7Zm9yKHZhciByPSIiLGk9MDtpPGU7aSsrKXIrPXQ7cmV0dXJuIHJ9dC5tb3ZlVG9DZWxsU2VxdWVuY2U9ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIHMsdT1yLmJ1ZmZlci54LGg9ci5idWZmZXIueTtpZighci5idWZmZXIuaGFzU2Nyb2xsYmFjaylyZXR1cm4gZnVuY3Rpb24oZSx0LHIsaSxzLHUpe3JldHVybiAwPT09bih0LGkscyx1KS5sZW5ndGg/IiI6bChhKGUsdCxlLHQtbyhzLHQpLCExLHMpLmxlbmd0aCxjKCJEIix1KSl9KHUsaCwwLHQscixpKStuKGgsdCxyLGkpK2Z1bmN0aW9uKGUsdCxyLGkscyx1KXt2YXIgaDtoPW4odCxpLHMsdSkubGVuZ3RoPjA/aS1vKHMsaSk6dDt2YXIgZj1pLF89ZnVuY3Rpb24oZSx0LHIsaSxzLGEpe3ZhciBjO3JldHVybiBjPW4ocixpLHMsYSkubGVuZ3RoPjA/aS1vKHMsaSk6dCxlPHImJmM8PWl8fGU+PXImJmM8aT8iQyI6IkQifShlLHQscixpLHMsdSk7cmV0dXJuIGwoYShlLGgscixmLCJDIj09PV8scykubGVuZ3RoLGMoXyx1KSl9KHUsaCxlLHQscixpKTtpZihoPT09dClyZXR1cm4gcz11PmU/IkQiOiJDIixsKE1hdGguYWJzKHUtZSksYyhzLGkpKTtzPWg+dD8iRCI6IkMiO3ZhciBmPU1hdGguYWJzKGgtdCk7cmV0dXJuIGwoZnVuY3Rpb24oZSx0KXtyZXR1cm4gdC5jb2xzLWV9KGg+dD9lOnUscikrKGYtMSkqci5jb2xzKzErKChoPnQ/dTplKS0xKSxjKHMsaSkpfX0sMTU0NjooZSx0LHIpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQmFzZVJlbmRlckxheWVyPXZvaWQgMDt2YXIgaT1yKDY0Myksbj1yKDg4MDMpLG89cigxNDIwKSxzPXIoMzczNCksYT1yKDE3NTIpLGM9cig0Nzc0KSxsPXIoOTYzMSksdT1yKDg5NzgpLGg9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyLGksbixvLHMsYSl7dGhpcy5fY29udGFpbmVyPWUsdGhpcy5fYWxwaGE9aSx0aGlzLl9jb2xvcnM9bix0aGlzLl9yZW5kZXJlcklkPW8sdGhpcy5fYnVmZmVyU2VydmljZT1zLHRoaXMuX29wdGlvbnNTZXJ2aWNlPWEsdGhpcy5fc2NhbGVkQ2hhcldpZHRoPTAsdGhpcy5fc2NhbGVkQ2hhckhlaWdodD0wLHRoaXMuX3NjYWxlZENlbGxXaWR0aD0wLHRoaXMuX3NjYWxlZENlbGxIZWlnaHQ9MCx0aGlzLl9zY2FsZWRDaGFyTGVmdD0wLHRoaXMuX3NjYWxlZENoYXJUb3A9MCx0aGlzLl9jdXJyZW50R2x5cGhJZGVudGlmaWVyPXtjaGFyczoiIixjb2RlOjAsYmc6MCxmZzowLGJvbGQ6ITEsZGltOiExLGl0YWxpYzohMX0sdGhpcy5fY2FudmFzPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImNhbnZhcyIpLHRoaXMuX2NhbnZhcy5jbGFzc0xpc3QuYWRkKCJ4dGVybS0iK3QrIi1sYXllciIpLHRoaXMuX2NhbnZhcy5zdHlsZS56SW5kZXg9ci50b1N0cmluZygpLHRoaXMuX2luaXRDYW52YXMoKSx0aGlzLl9jb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy5fY2FudmFzKX1yZXR1cm4gZS5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3ZhciBlOygwLGwucmVtb3ZlRWxlbWVudEZyb21QYXJlbnQpKHRoaXMuX2NhbnZhcyksbnVsbD09PShlPXRoaXMuX2NoYXJBdGxhcyl8fHZvaWQgMD09PWV8fGUuZGlzcG9zZSgpfSxlLnByb3RvdHlwZS5faW5pdENhbnZhcz1mdW5jdGlvbigpe3RoaXMuX2N0eD0oMCxhLnRocm93SWZGYWxzeSkodGhpcy5fY2FudmFzLmdldENvbnRleHQoIjJkIix7YWxwaGE6dGhpcy5fYWxwaGF9KSksdGhpcy5fYWxwaGF8fHRoaXMuX2NsZWFyQWxsKCl9LGUucHJvdG90eXBlLm9uT3B0aW9uc0NoYW5nZWQ9ZnVuY3Rpb24oKXt9LGUucHJvdG90eXBlLm9uQmx1cj1mdW5jdGlvbigpe30sZS5wcm90b3R5cGUub25Gb2N1cz1mdW5jdGlvbigpe30sZS5wcm90b3R5cGUub25DdXJzb3JNb3ZlPWZ1bmN0aW9uKCl7fSxlLnByb3RvdHlwZS5vbkdyaWRDaGFuZ2VkPWZ1bmN0aW9uKGUsdCl7fSxlLnByb3RvdHlwZS5vblNlbGVjdGlvbkNoYW5nZWQ9ZnVuY3Rpb24oZSx0LHIpe3ZvaWQgMD09PXImJihyPSExKX0sZS5wcm90b3R5cGUuc2V0Q29sb3JzPWZ1bmN0aW9uKGUpe3RoaXMuX3JlZnJlc2hDaGFyQXRsYXMoZSl9LGUucHJvdG90eXBlLl9zZXRUcmFuc3BhcmVuY3k9ZnVuY3Rpb24oZSl7aWYoZSE9PXRoaXMuX2FscGhhKXt2YXIgdD10aGlzLl9jYW52YXM7dGhpcy5fYWxwaGE9ZSx0aGlzLl9jYW52YXM9dGhpcy5fY2FudmFzLmNsb25lTm9kZSgpLHRoaXMuX2luaXRDYW52YXMoKSx0aGlzLl9jb250YWluZXIucmVwbGFjZUNoaWxkKHRoaXMuX2NhbnZhcyx0KSx0aGlzLl9yZWZyZXNoQ2hhckF0bGFzKHRoaXMuX2NvbG9ycyksdGhpcy5vbkdyaWRDaGFuZ2VkKDAsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEpfX0sZS5wcm90b3R5cGUuX3JlZnJlc2hDaGFyQXRsYXM9ZnVuY3Rpb24oZSl7dGhpcy5fc2NhbGVkQ2hhcldpZHRoPD0wJiZ0aGlzLl9zY2FsZWRDaGFySGVpZ2h0PD0wfHwodGhpcy5fY2hhckF0bGFzPSgwLG8uYWNxdWlyZUNoYXJBdGxhcykodGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucyx0aGlzLl9yZW5kZXJlcklkLGUsdGhpcy5fc2NhbGVkQ2hhcldpZHRoLHRoaXMuX3NjYWxlZENoYXJIZWlnaHQpLHRoaXMuX2NoYXJBdGxhcy53YXJtVXAoKSl9LGUucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlKXt0aGlzLl9zY2FsZWRDZWxsV2lkdGg9ZS5zY2FsZWRDZWxsV2lkdGgsdGhpcy5fc2NhbGVkQ2VsbEhlaWdodD1lLnNjYWxlZENlbGxIZWlnaHQsdGhpcy5fc2NhbGVkQ2hhcldpZHRoPWUuc2NhbGVkQ2hhcldpZHRoLHRoaXMuX3NjYWxlZENoYXJIZWlnaHQ9ZS5zY2FsZWRDaGFySGVpZ2h0LHRoaXMuX3NjYWxlZENoYXJMZWZ0PWUuc2NhbGVkQ2hhckxlZnQsdGhpcy5fc2NhbGVkQ2hhclRvcD1lLnNjYWxlZENoYXJUb3AsdGhpcy5fY2FudmFzLndpZHRoPWUuc2NhbGVkQ2FudmFzV2lkdGgsdGhpcy5fY2FudmFzLmhlaWdodD1lLnNjYWxlZENhbnZhc0hlaWdodCx0aGlzLl9jYW52YXMuc3R5bGUud2lkdGg9ZS5jYW52YXNXaWR0aCsicHgiLHRoaXMuX2NhbnZhcy5zdHlsZS5oZWlnaHQ9ZS5jYW52YXNIZWlnaHQrInB4Iix0aGlzLl9hbHBoYXx8dGhpcy5fY2xlYXJBbGwoKSx0aGlzLl9yZWZyZXNoQ2hhckF0bGFzKHRoaXMuX2NvbG9ycyl9LGUucHJvdG90eXBlLmNsZWFyVGV4dHVyZUF0bGFzPWZ1bmN0aW9uKCl7dmFyIGU7bnVsbD09PShlPXRoaXMuX2NoYXJBdGxhcyl8fHZvaWQgMD09PWV8fGUuY2xlYXIoKX0sZS5wcm90b3R5cGUuX2ZpbGxDZWxscz1mdW5jdGlvbihlLHQscixpKXt0aGlzLl9jdHguZmlsbFJlY3QoZSp0aGlzLl9zY2FsZWRDZWxsV2lkdGgsdCp0aGlzLl9zY2FsZWRDZWxsSGVpZ2h0LHIqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLGkqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCl9LGUucHJvdG90eXBlLl9maWxsTWlkZGxlTGluZUF0Q2VsbHM9ZnVuY3Rpb24oZSx0LHIpe3ZvaWQgMD09PXImJihyPTEpO3ZhciBpPU1hdGguY2VpbCguNSp0aGlzLl9zY2FsZWRDZWxsSGVpZ2h0KTt0aGlzLl9jdHguZmlsbFJlY3QoZSp0aGlzLl9zY2FsZWRDZWxsV2lkdGgsKHQrMSkqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodC1pLXdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHIqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKX0sZS5wcm90b3R5cGUuX2ZpbGxCb3R0b21MaW5lQXRDZWxscz1mdW5jdGlvbihlLHQscil7dm9pZCAwPT09ciYmKHI9MSksdGhpcy5fY3R4LmZpbGxSZWN0KGUqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLCh0KzEpKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQtd2luZG93LmRldmljZVBpeGVsUmF0aW8tMSxyKnRoaXMuX3NjYWxlZENlbGxXaWR0aCx3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyl9LGUucHJvdG90eXBlLl9maWxsTGVmdExpbmVBdENlbGw9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2N0eC5maWxsUmVjdChlKnRoaXMuX3NjYWxlZENlbGxXaWR0aCx0KnRoaXMuX3NjYWxlZENlbGxIZWlnaHQsd2luZG93LmRldmljZVBpeGVsUmF0aW8qcix0aGlzLl9zY2FsZWRDZWxsSGVpZ2h0KX0sZS5wcm90b3R5cGUuX3N0cm9rZVJlY3RBdENlbGw9ZnVuY3Rpb24oZSx0LHIsaSl7dGhpcy5fY3R4LmxpbmVXaWR0aD13aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyx0aGlzLl9jdHguc3Ryb2tlUmVjdChlKnRoaXMuX3NjYWxlZENlbGxXaWR0aCt3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpby8yLHQqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCt3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpby8yLHIqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLXdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLGkqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodC13aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyl9LGUucHJvdG90eXBlLl9jbGVhckFsbD1mdW5jdGlvbigpe3RoaXMuX2FscGhhP3RoaXMuX2N0eC5jbGVhclJlY3QoMCwwLHRoaXMuX2NhbnZhcy53aWR0aCx0aGlzLl9jYW52YXMuaGVpZ2h0KToodGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuYmFja2dyb3VuZC5jc3MsdGhpcy5fY3R4LmZpbGxSZWN0KDAsMCx0aGlzLl9jYW52YXMud2lkdGgsdGhpcy5fY2FudmFzLmhlaWdodCkpfSxlLnByb3RvdHlwZS5fY2xlYXJDZWxscz1mdW5jdGlvbihlLHQscixpKXt0aGlzLl9hbHBoYT90aGlzLl9jdHguY2xlYXJSZWN0KGUqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHQqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCxyKnRoaXMuX3NjYWxlZENlbGxXaWR0aCxpKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQpOih0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5iYWNrZ3JvdW5kLmNzcyx0aGlzLl9jdHguZmlsbFJlY3QoZSp0aGlzLl9zY2FsZWRDZWxsV2lkdGgsdCp0aGlzLl9zY2FsZWRDZWxsSGVpZ2h0LHIqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLGkqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCkpfSxlLnByb3RvdHlwZS5fZmlsbENoYXJUcnVlQ29sb3I9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2N0eC5mb250PXRoaXMuX2dldEZvbnQoITEsITEpLHRoaXMuX2N0eC50ZXh0QmFzZWxpbmU9bi5URVhUX0JBU0VMSU5FLHRoaXMuX2NsaXBSb3cocik7dmFyIGk9ITE7ITEhPT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmN1c3RvbUdseXBocyYmKGk9KDAsdS50cnlEcmF3Q3VzdG9tQ2hhcikodGhpcy5fY3R4LGUuZ2V0Q2hhcnMoKSx0KnRoaXMuX3NjYWxlZENlbGxXaWR0aCxyKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQsdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHRoaXMuX3NjYWxlZENlbGxIZWlnaHQpKSxpfHx0aGlzLl9jdHguZmlsbFRleHQoZS5nZXRDaGFycygpLHQqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoK3RoaXMuX3NjYWxlZENoYXJMZWZ0LHIqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCt0aGlzLl9zY2FsZWRDaGFyVG9wK3RoaXMuX3NjYWxlZENoYXJIZWlnaHQpfSxlLnByb3RvdHlwZS5fZHJhd0NoYXJzPWZ1bmN0aW9uKGUsdCxyKXt2YXIgbyxzLGEsYz10aGlzLl9nZXRDb250cmFzdENvbG9yKGUpO2N8fGUuaXNGZ1JHQigpfHxlLmlzQmdSR0IoKT90aGlzLl9kcmF3VW5jYWNoZWRDaGFycyhlLHQscixjKTooZS5pc0ludmVyc2UoKT8ocz1lLmlzQmdEZWZhdWx0KCk/bi5JTlZFUlRFRF9ERUZBVUxUX0NPTE9SOmUuZ2V0QmdDb2xvcigpLGE9ZS5pc0ZnRGVmYXVsdCgpP24uSU5WRVJURURfREVGQVVMVF9DT0xPUjplLmdldEZnQ29sb3IoKSk6KGE9ZS5pc0JnRGVmYXVsdCgpP2kuREVGQVVMVF9DT0xPUjplLmdldEJnQ29sb3IoKSxzPWUuaXNGZ0RlZmF1bHQoKT9pLkRFRkFVTFRfQ09MT1I6ZS5nZXRGZ0NvbG9yKCkpLHMrPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZHJhd0JvbGRUZXh0SW5CcmlnaHRDb2xvcnMmJmUuaXNCb2xkKCkmJnM8OD84OjAsdGhpcy5fY3VycmVudEdseXBoSWRlbnRpZmllci5jaGFycz1lLmdldENoYXJzKCl8fGkuV0hJVEVTUEFDRV9DRUxMX0NIQVIsdGhpcy5fY3VycmVudEdseXBoSWRlbnRpZmllci5jb2RlPWUuZ2V0Q29kZSgpfHxpLldISVRFU1BBQ0VfQ0VMTF9DT0RFLHRoaXMuX2N1cnJlbnRHbHlwaElkZW50aWZpZXIuYmc9YSx0aGlzLl9jdXJyZW50R2x5cGhJZGVudGlmaWVyLmZnPXMsdGhpcy5fY3VycmVudEdseXBoSWRlbnRpZmllci5ib2xkPSEhZS5pc0JvbGQoKSx0aGlzLl9jdXJyZW50R2x5cGhJZGVudGlmaWVyLmRpbT0hIWUuaXNEaW0oKSx0aGlzLl9jdXJyZW50R2x5cGhJZGVudGlmaWVyLml0YWxpYz0hIWUuaXNJdGFsaWMoKSwobnVsbD09PShvPXRoaXMuX2NoYXJBdGxhcyl8fHZvaWQgMD09PW8/dm9pZCAwOm8uZHJhdyh0aGlzLl9jdHgsdGhpcy5fY3VycmVudEdseXBoSWRlbnRpZmllcix0KnRoaXMuX3NjYWxlZENlbGxXaWR0aCt0aGlzLl9zY2FsZWRDaGFyTGVmdCxyKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQrdGhpcy5fc2NhbGVkQ2hhclRvcCkpfHx0aGlzLl9kcmF3VW5jYWNoZWRDaGFycyhlLHQscikpfSxlLnByb3RvdHlwZS5fZHJhd1VuY2FjaGVkQ2hhcnM9ZnVuY3Rpb24oZSx0LHIsaSl7aWYodGhpcy5fY3R4LnNhdmUoKSx0aGlzLl9jdHguZm9udD10aGlzLl9nZXRGb250KCEhZS5pc0JvbGQoKSwhIWUuaXNJdGFsaWMoKSksdGhpcy5fY3R4LnRleHRCYXNlbGluZT1uLlRFWFRfQkFTRUxJTkUsZS5pc0ludmVyc2UoKSlpZihpKXRoaXMuX2N0eC5maWxsU3R5bGU9aS5jc3M7ZWxzZSBpZihlLmlzQmdEZWZhdWx0KCkpdGhpcy5fY3R4LmZpbGxTdHlsZT1jLmNvbG9yLm9wYXF1ZSh0aGlzLl9jb2xvcnMuYmFja2dyb3VuZCkuY3NzO2Vsc2UgaWYoZS5pc0JnUkdCKCkpdGhpcy5fY3R4LmZpbGxTdHlsZT0icmdiKCIrcy5BdHRyaWJ1dGVEYXRhLnRvQ29sb3JSR0IoZS5nZXRCZ0NvbG9yKCkpLmpvaW4oIiwiKSsiKSI7ZWxzZXt2YXIgbz1lLmdldEJnQ29sb3IoKTt0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmRyYXdCb2xkVGV4dEluQnJpZ2h0Q29sb3JzJiZlLmlzQm9sZCgpJiZvPDgmJihvKz04KSx0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5hbnNpW29dLmNzc31lbHNlIGlmKGkpdGhpcy5fY3R4LmZpbGxTdHlsZT1pLmNzcztlbHNlIGlmKGUuaXNGZ0RlZmF1bHQoKSl0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5mb3JlZ3JvdW5kLmNzcztlbHNlIGlmKGUuaXNGZ1JHQigpKXRoaXMuX2N0eC5maWxsU3R5bGU9InJnYigiK3MuQXR0cmlidXRlRGF0YS50b0NvbG9yUkdCKGUuZ2V0RmdDb2xvcigpKS5qb2luKCIsIikrIikiO2Vsc2V7dmFyIGE9ZS5nZXRGZ0NvbG9yKCk7dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5kcmF3Qm9sZFRleHRJbkJyaWdodENvbG9ycyYmZS5pc0JvbGQoKSYmYTw4JiYoYSs9OCksdGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuYW5zaVthXS5jc3N9dGhpcy5fY2xpcFJvdyhyKSxlLmlzRGltKCkmJih0aGlzLl9jdHguZ2xvYmFsQWxwaGE9bi5ESU1fT1BBQ0lUWSk7dmFyIGw9ITE7ITEhPT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmN1c3RvbUdseXBocyYmKGw9KDAsdS50cnlEcmF3Q3VzdG9tQ2hhcikodGhpcy5fY3R4LGUuZ2V0Q2hhcnMoKSx0KnRoaXMuX3NjYWxlZENlbGxXaWR0aCxyKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQsdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHRoaXMuX3NjYWxlZENlbGxIZWlnaHQpKSxsfHx0aGlzLl9jdHguZmlsbFRleHQoZS5nZXRDaGFycygpLHQqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoK3RoaXMuX3NjYWxlZENoYXJMZWZ0LHIqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCt0aGlzLl9zY2FsZWRDaGFyVG9wK3RoaXMuX3NjYWxlZENoYXJIZWlnaHQpLHRoaXMuX2N0eC5yZXN0b3JlKCl9LGUucHJvdG90eXBlLl9jbGlwUm93PWZ1bmN0aW9uKGUpe3RoaXMuX2N0eC5iZWdpblBhdGgoKSx0aGlzLl9jdHgucmVjdCgwLGUqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHRoaXMuX3NjYWxlZENlbGxIZWlnaHQpLHRoaXMuX2N0eC5jbGlwKCl9LGUucHJvdG90eXBlLl9nZXRGb250PWZ1bmN0aW9uKGUsdCl7cmV0dXJuKHQ/Iml0YWxpYyI6IiIpKyIgIisoZT90aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZvbnRXZWlnaHRCb2xkOnRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZm9udFdlaWdodCkrIiAiK3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZm9udFNpemUqd2luZG93LmRldmljZVBpeGVsUmF0aW8rInB4ICIrdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5mb250RmFtaWx5fSxlLnByb3RvdHlwZS5fZ2V0Q29udHJhc3RDb2xvcj1mdW5jdGlvbihlKXtpZigxIT09dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5taW5pbXVtQ29udHJhc3RSYXRpbyl7dmFyIHQ9dGhpcy5fY29sb3JzLmNvbnRyYXN0Q2FjaGUuZ2V0Q29sb3IoZS5iZyxlLmZnKTtpZih2b2lkIDAhPT10KXJldHVybiB0fHx2b2lkIDA7dmFyIHI9ZS5nZXRGZ0NvbG9yKCksaT1lLmdldEZnQ29sb3JNb2RlKCksbj1lLmdldEJnQ29sb3IoKSxvPWUuZ2V0QmdDb2xvck1vZGUoKSxzPSEhZS5pc0ludmVyc2UoKSxhPSEhZS5pc0ludmVyc2UoKTtpZihzKXt2YXIgbD1yO3I9bixuPWw7dmFyIHU9aTtpPW8sbz11fXZhciBoPXRoaXMuX3Jlc29sdmVCYWNrZ3JvdW5kUmdiYShvLG4scyksZj10aGlzLl9yZXNvbHZlRm9yZWdyb3VuZFJnYmEoaSxyLHMsYSksXz1jLnJnYmEuZW5zdXJlQ29udHJhc3RSYXRpbyhoLGYsdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5taW5pbXVtQ29udHJhc3RSYXRpbyk7aWYoXyl7dmFyIGQ9e2NzczpjLmNoYW5uZWxzLnRvQ3NzKF8+PjI0JjI1NSxfPj4xNiYyNTUsXz4+OCYyNTUpLHJnYmE6X307cmV0dXJuIHRoaXMuX2NvbG9ycy5jb250cmFzdENhY2hlLnNldENvbG9yKGUuYmcsZS5mZyxkKSxkfXRoaXMuX2NvbG9ycy5jb250cmFzdENhY2hlLnNldENvbG9yKGUuYmcsZS5mZyxudWxsKX19LGUucHJvdG90eXBlLl9yZXNvbHZlQmFja2dyb3VuZFJnYmE9ZnVuY3Rpb24oZSx0LHIpe3N3aXRjaChlKXtjYXNlIDE2Nzc3MjE2OmNhc2UgMzM1NTQ0MzI6cmV0dXJuIHRoaXMuX2NvbG9ycy5hbnNpW3RdLnJnYmE7Y2FzZSA1MDMzMTY0ODpyZXR1cm4gdDw8ODtkZWZhdWx0OnJldHVybiByP3RoaXMuX2NvbG9ycy5mb3JlZ3JvdW5kLnJnYmE6dGhpcy5fY29sb3JzLmJhY2tncm91bmQucmdiYX19LGUucHJvdG90eXBlLl9yZXNvbHZlRm9yZWdyb3VuZFJnYmE9ZnVuY3Rpb24oZSx0LHIsaSl7c3dpdGNoKGUpe2Nhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpyZXR1cm4gdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5kcmF3Qm9sZFRleHRJbkJyaWdodENvbG9ycyYmaSYmdDw4JiYodCs9OCksdGhpcy5fY29sb3JzLmFuc2lbdF0ucmdiYTtjYXNlIDUwMzMxNjQ4OnJldHVybiB0PDw4O2RlZmF1bHQ6cmV0dXJuIHI/dGhpcy5fY29sb3JzLmJhY2tncm91bmQucmdiYTp0aGlzLl9jb2xvcnMuZm9yZWdyb3VuZC5yZ2JhfX0sZX0oKTt0LkJhc2VSZW5kZXJMYXllcj1ofSwyNTEyOmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkN1cnNvclJlbmRlckxheWVyPXZvaWQgMDt2YXIgYT1yKDE1NDYpLGM9cig1MTEpLGw9cigyNTg1KSx1PXIoNDcyNSksaD02MDAsZj1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpLG4sbyxzLGEsbCx1KXt2YXIgaD1lLmNhbGwodGhpcyx0LCJjdXJzb3IiLHIsITAsaSxuLHMsYSl8fHRoaXM7cmV0dXJuIGguX29uUmVxdWVzdFJlZHJhdz1vLGguX2NvcmVTZXJ2aWNlPWwsaC5fY29yZUJyb3dzZXJTZXJ2aWNlPXUsaC5fY2VsbD1uZXcgYy5DZWxsRGF0YSxoLl9zdGF0ZT17eDowLHk6MCxpc0ZvY3VzZWQ6ITEsc3R5bGU6IiIsd2lkdGg6MH0saC5fY3Vyc29yUmVuZGVyZXJzPXtiYXI6aC5fcmVuZGVyQmFyQ3Vyc29yLmJpbmQoaCksYmxvY2s6aC5fcmVuZGVyQmxvY2tDdXJzb3IuYmluZChoKSx1bmRlcmxpbmU6aC5fcmVuZGVyVW5kZXJsaW5lQ3Vyc29yLmJpbmQoaCl9LGh9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXImJih0aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlci5kaXNwb3NlKCksdGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXI9dm9pZCAwKSxlLnByb3RvdHlwZS5kaXNwb3NlLmNhbGwodGhpcyl9LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbih0KXtlLnByb3RvdHlwZS5yZXNpemUuY2FsbCh0aGlzLHQpLHRoaXMuX3N0YXRlPXt4OjAseTowLGlzRm9jdXNlZDohMSxzdHlsZToiIix3aWR0aDowfX0sdC5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt2YXIgZTt0aGlzLl9jbGVhckN1cnNvcigpLG51bGw9PT0oZT10aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcil8fHZvaWQgMD09PWV8fGUucmVzdGFydEJsaW5rQW5pbWF0aW9uKCksdGhpcy5vbk9wdGlvbnNDaGFuZ2VkKCl9LHQucHJvdG90eXBlLm9uQmx1cj1mdW5jdGlvbigpe3ZhciBlO251bGw9PT0oZT10aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcil8fHZvaWQgMD09PWV8fGUucGF1c2UoKSx0aGlzLl9vblJlcXVlc3RSZWRyYXcuZmlyZSh7c3RhcnQ6dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueSxlbmQ6dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueX0pfSx0LnByb3RvdHlwZS5vbkZvY3VzPWZ1bmN0aW9uKCl7dmFyIGU7bnVsbD09PShlPXRoaXMuX2N1cnNvckJsaW5rU3RhdGVNYW5hZ2VyKXx8dm9pZCAwPT09ZXx8ZS5yZXN1bWUoKSx0aGlzLl9vblJlcXVlc3RSZWRyYXcuZmlyZSh7c3RhcnQ6dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueSxlbmQ6dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueX0pfSx0LnByb3RvdHlwZS5vbk9wdGlvbnNDaGFuZ2VkPWZ1bmN0aW9uKCl7dmFyIGUsdD10aGlzO3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yQmxpbms/dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXJ8fCh0aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcj1uZXcgXyh0aGlzLl9jb3JlQnJvd3NlclNlcnZpY2UuaXNGb2N1c2VkLChmdW5jdGlvbigpe3QuX3JlbmRlcighMCl9KSkpOihudWxsPT09KGU9dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXIpfHx2b2lkIDA9PT1lfHxlLmRpc3Bvc2UoKSx0aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcj12b2lkIDApLHRoaXMuX29uUmVxdWVzdFJlZHJhdy5maXJlKHtzdGFydDp0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55LGVuZDp0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55fSl9LHQucHJvdG90eXBlLm9uQ3Vyc29yTW92ZT1mdW5jdGlvbigpe3ZhciBlO251bGw9PT0oZT10aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcil8fHZvaWQgMD09PWV8fGUucmVzdGFydEJsaW5rQW5pbWF0aW9uKCl9LHQucHJvdG90eXBlLm9uR3JpZENoYW5nZWQ9ZnVuY3Rpb24oZSx0KXshdGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXJ8fHRoaXMuX2N1cnNvckJsaW5rU3RhdGVNYW5hZ2VyLmlzUGF1c2VkP3RoaXMuX3JlbmRlcighMSk6dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXIucmVzdGFydEJsaW5rQW5pbWF0aW9uKCl9LHQucHJvdG90eXBlLl9yZW5kZXI9ZnVuY3Rpb24oZSl7aWYodGhpcy5fY29yZVNlcnZpY2UuaXNDdXJzb3JJbml0aWFsaXplZCYmIXRoaXMuX2NvcmVTZXJ2aWNlLmlzQ3Vyc29ySGlkZGVuKXt2YXIgdD10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55YmFzZSt0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55LHI9dC10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcDtpZihyPDB8fHI+PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyl0aGlzLl9jbGVhckN1cnNvcigpO2Vsc2V7dmFyIGk9TWF0aC5taW4odGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueCx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtMSk7aWYodGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KHQpLmxvYWRDZWxsKGksdGhpcy5fY2VsbCksdm9pZCAwIT09dGhpcy5fY2VsbC5jb250ZW50KXtpZighdGhpcy5fY29yZUJyb3dzZXJTZXJ2aWNlLmlzRm9jdXNlZCl7dGhpcy5fY2xlYXJDdXJzb3IoKSx0aGlzLl9jdHguc2F2ZSgpLHRoaXMuX2N0eC5maWxsU3R5bGU9dGhpcy5fY29sb3JzLmN1cnNvci5jc3M7dmFyIG49dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JTdHlsZTtyZXR1cm4gbiYmImJsb2NrIiE9PW4/dGhpcy5fY3Vyc29yUmVuZGVyZXJzW25dKGkscix0aGlzLl9jZWxsKTp0aGlzLl9yZW5kZXJCbHVyQ3Vyc29yKGkscix0aGlzLl9jZWxsKSx0aGlzLl9jdHgucmVzdG9yZSgpLHRoaXMuX3N0YXRlLng9aSx0aGlzLl9zdGF0ZS55PXIsdGhpcy5fc3RhdGUuaXNGb2N1c2VkPSExLHRoaXMuX3N0YXRlLnN0eWxlPW4sdm9pZCh0aGlzLl9zdGF0ZS53aWR0aD10aGlzLl9jZWxsLmdldFdpZHRoKCkpfWlmKCF0aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcnx8dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXIuaXNDdXJzb3JWaXNpYmxlKXtpZih0aGlzLl9zdGF0ZSl7aWYodGhpcy5fc3RhdGUueD09PWkmJnRoaXMuX3N0YXRlLnk9PT1yJiZ0aGlzLl9zdGF0ZS5pc0ZvY3VzZWQ9PT10aGlzLl9jb3JlQnJvd3NlclNlcnZpY2UuaXNGb2N1c2VkJiZ0aGlzLl9zdGF0ZS5zdHlsZT09PXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGUmJnRoaXMuX3N0YXRlLndpZHRoPT09dGhpcy5fY2VsbC5nZXRXaWR0aCgpKXJldHVybjt0aGlzLl9jbGVhckN1cnNvcigpfXRoaXMuX2N0eC5zYXZlKCksdGhpcy5fY3Vyc29yUmVuZGVyZXJzW3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGV8fCJibG9jayJdKGkscix0aGlzLl9jZWxsKSx0aGlzLl9jdHgucmVzdG9yZSgpLHRoaXMuX3N0YXRlLng9aSx0aGlzLl9zdGF0ZS55PXIsdGhpcy5fc3RhdGUuaXNGb2N1c2VkPSExLHRoaXMuX3N0YXRlLnN0eWxlPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGUsdGhpcy5fc3RhdGUud2lkdGg9dGhpcy5fY2VsbC5nZXRXaWR0aCgpfWVsc2UgdGhpcy5fY2xlYXJDdXJzb3IoKX19fWVsc2UgdGhpcy5fY2xlYXJDdXJzb3IoKX0sdC5wcm90b3R5cGUuX2NsZWFyQ3Vyc29yPWZ1bmN0aW9uKCl7dGhpcy5fc3RhdGUmJih3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbzwxP3RoaXMuX2NsZWFyQWxsKCk6dGhpcy5fY2xlYXJDZWxscyh0aGlzLl9zdGF0ZS54LHRoaXMuX3N0YXRlLnksdGhpcy5fc3RhdGUud2lkdGgsMSksdGhpcy5fc3RhdGU9e3g6MCx5OjAsaXNGb2N1c2VkOiExLHN0eWxlOiIiLHdpZHRoOjB9KX0sdC5wcm90b3R5cGUuX3JlbmRlckJhckN1cnNvcj1mdW5jdGlvbihlLHQscil7dGhpcy5fY3R4LnNhdmUoKSx0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5jdXJzb3IuY3NzLHRoaXMuX2ZpbGxMZWZ0TGluZUF0Q2VsbChlLHQsdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JXaWR0aCksdGhpcy5fY3R4LnJlc3RvcmUoKX0sdC5wcm90b3R5cGUuX3JlbmRlckJsb2NrQ3Vyc29yPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9jdHguc2F2ZSgpLHRoaXMuX2N0eC5maWxsU3R5bGU9dGhpcy5fY29sb3JzLmN1cnNvci5jc3MsdGhpcy5fZmlsbENlbGxzKGUsdCxyLmdldFdpZHRoKCksMSksdGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuY3Vyc29yQWNjZW50LmNzcyx0aGlzLl9maWxsQ2hhclRydWVDb2xvcihyLGUsdCksdGhpcy5fY3R4LnJlc3RvcmUoKX0sdC5wcm90b3R5cGUuX3JlbmRlclVuZGVybGluZUN1cnNvcj1mdW5jdGlvbihlLHQscil7dGhpcy5fY3R4LnNhdmUoKSx0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5jdXJzb3IuY3NzLHRoaXMuX2ZpbGxCb3R0b21MaW5lQXRDZWxscyhlLHQpLHRoaXMuX2N0eC5yZXN0b3JlKCl9LHQucHJvdG90eXBlLl9yZW5kZXJCbHVyQ3Vyc29yPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9jdHguc2F2ZSgpLHRoaXMuX2N0eC5zdHJva2VTdHlsZT10aGlzLl9jb2xvcnMuY3Vyc29yLmNzcyx0aGlzLl9zdHJva2VSZWN0QXRDZWxsKGUsdCxyLmdldFdpZHRoKCksMSksdGhpcy5fY3R4LnJlc3RvcmUoKX0sbyhbcyg1LGwuSUJ1ZmZlclNlcnZpY2UpLHMoNixsLklPcHRpb25zU2VydmljZSkscyg3LGwuSUNvcmVTZXJ2aWNlKSxzKDgsdS5JQ29yZUJyb3dzZXJTZXJ2aWNlKV0sdCl9KGEuQmFzZVJlbmRlckxheWVyKTt0LkN1cnNvclJlbmRlckxheWVyPWY7dmFyIF89ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCl7dGhpcy5fcmVuZGVyQ2FsbGJhY2s9dCx0aGlzLmlzQ3Vyc29yVmlzaWJsZT0hMCxlJiZ0aGlzLl9yZXN0YXJ0SW50ZXJ2YWwoKX1yZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJpc1BhdXNlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiEodGhpcy5fYmxpbmtTdGFydFRpbWVvdXR8fHRoaXMuX2JsaW5rSW50ZXJ2YWwpfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt0aGlzLl9ibGlua0ludGVydmFsJiYod2luZG93LmNsZWFySW50ZXJ2YWwodGhpcy5fYmxpbmtJbnRlcnZhbCksdGhpcy5fYmxpbmtJbnRlcnZhbD12b2lkIDApLHRoaXMuX2JsaW5rU3RhcnRUaW1lb3V0JiYod2luZG93LmNsZWFyVGltZW91dCh0aGlzLl9ibGlua1N0YXJ0VGltZW91dCksdGhpcy5fYmxpbmtTdGFydFRpbWVvdXQ9dm9pZCAwKSx0aGlzLl9hbmltYXRpb25GcmFtZSYmKHdpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZSh0aGlzLl9hbmltYXRpb25GcmFtZSksdGhpcy5fYW5pbWF0aW9uRnJhbWU9dm9pZCAwKX0sZS5wcm90b3R5cGUucmVzdGFydEJsaW5rQW5pbWF0aW9uPWZ1bmN0aW9uKCl7dmFyIGU9dGhpczt0aGlzLmlzUGF1c2VkfHwodGhpcy5fYW5pbWF0aW9uVGltZVJlc3RhcnRlZD1EYXRlLm5vdygpLHRoaXMuaXNDdXJzb3JWaXNpYmxlPSEwLHRoaXMuX2FuaW1hdGlvbkZyYW1lfHwodGhpcy5fYW5pbWF0aW9uRnJhbWU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoZnVuY3Rpb24oKXtlLl9yZW5kZXJDYWxsYmFjaygpLGUuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMH0pKSkpfSxlLnByb3RvdHlwZS5fcmVzdGFydEludGVydmFsPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7dm9pZCAwPT09ZSYmKGU9aCksdGhpcy5fYmxpbmtJbnRlcnZhbCYmKHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuX2JsaW5rSW50ZXJ2YWwpLHRoaXMuX2JsaW5rSW50ZXJ2YWw9dm9pZCAwKSx0aGlzLl9ibGlua1N0YXJ0VGltZW91dD13aW5kb3cuc2V0VGltZW91dCgoZnVuY3Rpb24oKXtpZih0Ll9hbmltYXRpb25UaW1lUmVzdGFydGVkKXt2YXIgZT1oLShEYXRlLm5vdygpLXQuX2FuaW1hdGlvblRpbWVSZXN0YXJ0ZWQpO2lmKHQuX2FuaW1hdGlvblRpbWVSZXN0YXJ0ZWQ9dm9pZCAwLGU+MClyZXR1cm4gdm9pZCB0Ll9yZXN0YXJ0SW50ZXJ2YWwoZSl9dC5pc0N1cnNvclZpc2libGU9ITEsdC5fYW5pbWF0aW9uRnJhbWU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoZnVuY3Rpb24oKXt0Ll9yZW5kZXJDYWxsYmFjaygpLHQuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMH0pKSx0Ll9ibGlua0ludGVydmFsPXdpbmRvdy5zZXRJbnRlcnZhbCgoZnVuY3Rpb24oKXtpZih0Ll9hbmltYXRpb25UaW1lUmVzdGFydGVkKXt2YXIgZT1oLShEYXRlLm5vdygpLXQuX2FuaW1hdGlvblRpbWVSZXN0YXJ0ZWQpO3JldHVybiB0Ll9hbmltYXRpb25UaW1lUmVzdGFydGVkPXZvaWQgMCx2b2lkIHQuX3Jlc3RhcnRJbnRlcnZhbChlKX10LmlzQ3Vyc29yVmlzaWJsZT0hdC5pc0N1cnNvclZpc2libGUsdC5fYW5pbWF0aW9uRnJhbWU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoZnVuY3Rpb24oKXt0Ll9yZW5kZXJDYWxsYmFjaygpLHQuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMH0pKX0pLGgpfSksZSl9LGUucHJvdG90eXBlLnBhdXNlPWZ1bmN0aW9uKCl7dGhpcy5pc0N1cnNvclZpc2libGU9ITAsdGhpcy5fYmxpbmtJbnRlcnZhbCYmKHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuX2JsaW5rSW50ZXJ2YWwpLHRoaXMuX2JsaW5rSW50ZXJ2YWw9dm9pZCAwKSx0aGlzLl9ibGlua1N0YXJ0VGltZW91dCYmKHdpbmRvdy5jbGVhclRpbWVvdXQodGhpcy5fYmxpbmtTdGFydFRpbWVvdXQpLHRoaXMuX2JsaW5rU3RhcnRUaW1lb3V0PXZvaWQgMCksdGhpcy5fYW5pbWF0aW9uRnJhbWUmJih3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUodGhpcy5fYW5pbWF0aW9uRnJhbWUpLHRoaXMuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMCl9LGUucHJvdG90eXBlLnJlc3VtZT1mdW5jdGlvbigpe3RoaXMucGF1c2UoKSx0aGlzLl9hbmltYXRpb25UaW1lUmVzdGFydGVkPXZvaWQgMCx0aGlzLl9yZXN0YXJ0SW50ZXJ2YWwoKSx0aGlzLnJlc3RhcnRCbGlua0FuaW1hdGlvbigpfSxlfSgpfSw4OTc4OihlLHQscik9Pnt2YXIgaSxuLG8scyxhLGMsbCx1LGgsZixfLGQscCx2LGcseSxtLGIsUyxDLHcsTCxFLHgsQSxrLE0sUixULE8sQixELFAsSSxILGosRixXLFUscSxOLHosSyxWLEcsWSxYLFosSiwkLFEsZWUsdGUscmUsaWUsbmUsb2Usc2UsYWUsY2UsbGUsdWUsaGUsZmUsX2UsZGUscGUsdmUsZ2UseWUsbWUsYmUsU2UsQ2Usd2UsTGUsRWUseGUsQWUsa2UsTWUsUmUsVGUsT2UsQmUsRGUsUGUsSWUsSGUsamUsRmUsV2UsVWUscWUsTmUsemUsS2UsVmUsR2UsWWUsWGUsWmUsSmUsJGUsUWUsZXQsdHQscnQsaXQsbnQsb3Qsc3QsYXQsY3QsbHQsdXQsaHQsZnQsX3QsZHQscHQsdnQsZ3QseXQsbXQsYnQsU3QsQ3Q7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQudHJ5RHJhd0N1c3RvbUNoYXI9dC5ib3hEcmF3aW5nRGVmaW5pdGlvbnM9dC5ibG9ja0VsZW1lbnREZWZpbml0aW9ucz12b2lkIDA7dmFyIHd0PXIoMTc1Mik7dC5ibG9ja0VsZW1lbnREZWZpbml0aW9ucz17IuKWgCI6W3t4OjAseTowLHc6OCxoOjR9XSwi4paBIjpbe3g6MCx5Ojcsdzo4LGg6MX1dLCLiloIiOlt7eDowLHk6Nix3OjgsaDoyfV0sIuKWgyI6W3t4OjAseTo1LHc6OCxoOjN9XSwi4paEIjpbe3g6MCx5OjQsdzo4LGg6NH1dLCLiloUiOlt7eDowLHk6Myx3OjgsaDo1fV0sIuKWhiI6W3t4OjAseToyLHc6OCxoOjZ9XSwi4paHIjpbe3g6MCx5OjEsdzo4LGg6N31dLCLilogiOlt7eDowLHk6MCx3OjgsaDo4fV0sIuKWiSI6W3t4OjAseTowLHc6NyxoOjh9XSwi4paKIjpbe3g6MCx5OjAsdzo2LGg6OH1dLCLilosiOlt7eDowLHk6MCx3OjUsaDo4fV0sIuKWjCI6W3t4OjAseTowLHc6NCxoOjh9XSwi4paNIjpbe3g6MCx5OjAsdzozLGg6OH1dLCLilo4iOlt7eDowLHk6MCx3OjIsaDo4fV0sIuKWjyI6W3t4OjAseTowLHc6MSxoOjh9XSwi4paQIjpbe3g6NCx5OjAsdzo0LGg6OH1dLCLilpQiOlt7eDowLHk6MCx3OjksaDoxfV0sIuKWlSI6W3t4OjcseTowLHc6MSxoOjh9XSwi4paWIjpbe3g6MCx5OjQsdzo0LGg6NH1dLCLilpciOlt7eDo0LHk6NCx3OjQsaDo0fV0sIuKWmCI6W3t4OjAseTowLHc6NCxoOjR9XSwi4paZIjpbe3g6MCx5OjAsdzo0LGg6OH0se3g6MCx5OjQsdzo4LGg6NH1dLCLilpoiOlt7eDowLHk6MCx3OjQsaDo0fSx7eDo0LHk6NCx3OjQsaDo0fV0sIuKWmyI6W3t4OjAseTowLHc6NCxoOjh9LHt4OjAseTowLHc6NCxoOjh9XSwi4pacIjpbe3g6MCx5OjAsdzo4LGg6NH0se3g6NCx5OjAsdzo0LGg6OH1dLCLilp0iOlt7eDo0LHk6MCx3OjQsaDo0fV0sIuKWniI6W3t4OjQseTowLHc6NCxoOjR9LHt4OjAseTo0LHc6NCxoOjR9XSwi4pafIjpbe3g6NCx5OjAsdzo0LGg6OH0se3g6MCx5OjQsdzo4LGg6NH1dLCLwn62wIjpbe3g6MSx5OjAsdzoxLGg6OH1dLCLwn62xIjpbe3g6Mix5OjAsdzoxLGg6OH1dLCLwn62yIjpbe3g6Myx5OjAsdzoxLGg6OH1dLCLwn62zIjpbe3g6NCx5OjAsdzoxLGg6OH1dLCLwn620Ijpbe3g6NSx5OjAsdzoxLGg6OH1dLCLwn621Ijpbe3g6Nix5OjAsdzoxLGg6OH1dLCLwn622Ijpbe3g6MCx5OjEsdzo4LGg6MX1dLCLwn623Ijpbe3g6MCx5OjIsdzo4LGg6MX1dLCLwn624Ijpbe3g6MCx5OjMsdzo4LGg6MX1dLCLwn625Ijpbe3g6MCx5OjQsdzo4LGg6MX1dLCLwn626Ijpbe3g6MCx5OjUsdzo4LGg6MX1dLCLwn627Ijpbe3g6MCx5OjYsdzo4LGg6MX1dLCLwn628Ijpbe3g6MCx5OjAsdzoxLGg6OH0se3g6MCx5Ojcsdzo4LGg6MX1dLCLwn629Ijpbe3g6MCx5OjAsdzoxLGg6OH0se3g6MCx5OjAsdzo4LGg6MX1dLCLwn62+Ijpbe3g6Nyx5OjAsdzoxLGg6OH0se3g6MCx5OjAsdzo4LGg6MX1dLCLwn62/Ijpbe3g6Nyx5OjAsdzoxLGg6OH0se3g6MCx5Ojcsdzo4LGg6MX1dLCLwn66AIjpbe3g6MCx5OjAsdzo4LGg6MX0se3g6MCx5Ojcsdzo4LGg6MX1dLCLwn66BIjpbe3g6MCx5OjAsdzo4LGg6MX0se3g6MCx5OjIsdzo4LGg6MX0se3g6MCx5OjQsdzo4LGg6MX0se3g6MCx5Ojcsdzo4LGg6MX1dLCLwn66CIjpbe3g6MCx5OjAsdzo4LGg6Mn1dLCLwn66DIjpbe3g6MCx5OjAsdzo4LGg6M31dLCLwn66EIjpbe3g6MCx5OjAsdzo4LGg6NX1dLCLwn66FIjpbe3g6MCx5OjAsdzo4LGg6Nn1dLCLwn66GIjpbe3g6MCx5OjAsdzo4LGg6N31dLCLwn66HIjpbe3g6Nix5OjAsdzoyLGg6OH1dLCLwn66IIjpbe3g6NSx5OjAsdzozLGg6OH1dLCLwn66JIjpbe3g6Myx5OjAsdzo1LGg6OH1dLCLwn66KIjpbe3g6Mix5OjAsdzo2LGg6OH1dLCLwn66LIjpbe3g6MSx5OjAsdzo3LGg6OH1dLCLwn66VIjpbe3g6MCx5OjAsdzoyLGg6Mn0se3g6NCx5OjAsdzoyLGg6Mn0se3g6Mix5OjIsdzoyLGg6Mn0se3g6Nix5OjIsdzoyLGg6Mn0se3g6MCx5OjQsdzoyLGg6Mn0se3g6NCx5OjQsdzoyLGg6Mn0se3g6Mix5OjYsdzoyLGg6Mn0se3g6Nix5OjYsdzoyLGg6Mn1dLCLwn66WIjpbe3g6Mix5OjAsdzoyLGg6Mn0se3g6Nix5OjAsdzoyLGg6Mn0se3g6MCx5OjIsdzoyLGg6Mn0se3g6NCx5OjIsdzoyLGg6Mn0se3g6Mix5OjQsdzoyLGg6Mn0se3g6Nix5OjQsdzoyLGg6Mn0se3g6MCx5OjYsdzoyLGg6Mn0se3g6NCx5OjYsdzoyLGg6Mn1dLCLwn66XIjpbe3g6MCx5OjIsdzo4LGg6Mn0se3g6MCx5OjYsdzo4LGg6Mn1dfTt2YXIgTHQ9eyLilpEiOltbMSwwLDAsMF0sWzAsMCwwLDBdLFswLDAsMSwwXSxbMCwwLDAsMF1dLCLilpIiOltbMSwwXSxbMCwwXSxbMCwxXSxbMCwwXV0sIuKWkyI6W1swLDFdLFsxLDFdLFsxLDBdLFsxLDFdXX07dC5ib3hEcmF3aW5nRGVmaW5pdGlvbnM9eyLilIAiOihpPXt9LGlbMV09Ik0wLC41IEwxLC41IixpKSwi4pSBIjoobj17fSxuWzNdPSJNMCwuNSBMMSwuNSIsbiksIuKUgiI6KG89e30sb1sxXT0iTS41LDAgTC41LDEiLG8pLCLilIMiOihzPXt9LHNbM109Ik0uNSwwIEwuNSwxIixzKSwi4pSMIjooYT17fSxhWzFdPSJNMC41LDEgTC41LC41IEwxLC41IixhKSwi4pSPIjooYz17fSxjWzNdPSJNMC41LDEgTC41LC41IEwxLC41IixjKSwi4pSQIjoobD17fSxsWzFdPSJNMCwuNSBMLjUsLjUgTC41LDEiLGwpLCLilJMiOih1PXt9LHVbM109Ik0wLC41IEwuNSwuNSBMLjUsMSIsdSksIuKUlCI6KGg9e30saFsxXT0iTS41LDAgTC41LC41IEwxLC41IixoKSwi4pSXIjooZj17fSxmWzNdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLGYpLCLilJgiOihfPXt9LF9bMV09Ik0uNSwwIEwuNSwuNSBMMCwuNSIsXyksIuKUmyI6KGQ9e30sZFszXT0iTS41LDAgTC41LC41IEwwLC41IixkKSwi4pScIjoocD17fSxwWzFdPSJNLjUsMCBMLjUsMSBNLjUsLjUgTDEsLjUiLHApLCLilKMiOih2PXt9LHZbM109Ik0uNSwwIEwuNSwxIE0uNSwuNSBMMSwuNSIsdiksIuKUpCI6KGc9e30sZ1sxXT0iTS41LDAgTC41LDEgTS41LC41IEwwLC41IixnKSwi4pSrIjooeT17fSx5WzNdPSJNLjUsMCBMLjUsMSBNLjUsLjUgTDAsLjUiLHkpLCLilKwiOihtPXt9LG1bMV09Ik0wLC41IEwxLC41IE0uNSwuNSBMLjUsMSIsbSksIuKUsyI6KGI9e30sYlszXT0iTTAsLjUgTDEsLjUgTS41LC41IEwuNSwxIixiKSwi4pS0IjooUz17fSxTWzFdPSJNMCwuNSBMMSwuNSBNLjUsLjUgTC41LDAiLFMpLCLilLsiOihDPXt9LENbM109Ik0wLC41IEwxLC41IE0uNSwuNSBMLjUsMCIsQyksIuKUvCI6KHc9e30sd1sxXT0iTTAsLjUgTDEsLjUgTS41LDAgTC41LDEiLHcpLCLilYsiOihMPXt9LExbM109Ik0wLC41IEwxLC41IE0uNSwwIEwuNSwxIixMKSwi4pW0IjooRT17fSxFWzFdPSJNLjUsLjUgTDAsLjUiLEUpLCLilbgiOih4PXt9LHhbM109Ik0uNSwuNSBMMCwuNSIseCksIuKVtSI6KEE9e30sQVsxXT0iTS41LC41IEwuNSwwIixBKSwi4pW5Ijooaz17fSxrWzNdPSJNLjUsLjUgTC41LDAiLGspLCLilbYiOihNPXt9LE1bMV09Ik0uNSwuNSBMMSwuNSIsTSksIuKVuiI6KFI9e30sUlszXT0iTS41LC41IEwxLC41IixSKSwi4pW3IjooVD17fSxUWzFdPSJNLjUsLjUgTC41LDEiLFQpLCLilbsiOihPPXt9LE9bM109Ik0uNSwuNSBMLjUsMSIsTyksIuKVkCI6KEI9e30sQlsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwiKyguNS10KSsiIEwxLCIrKC41LXQpKyIgTTAsIisoLjUrdCkrIiBMMSwiKyguNSt0KX0sQiksIuKVkSI6KEQ9e30sRFsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNIisoLjUtZSkrIiwwIEwiKyguNS1lKSsiLDEgTSIrKC41K2UpKyIsMCBMIisoLjUrZSkrIiwxIn0sRCksIuKVkiI6KFA9e30sUFsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNLjUsMSBMLjUsIisoLjUtdCkrIiBMMSwiKyguNS10KSsiIE0uNSwiKyguNSt0KSsiIEwxLCIrKC41K3QpfSxQKSwi4pWTIjooST17fSxJWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0iKyguNS1lKSsiLDEgTCIrKC41LWUpKyIsLjUgTDEsLjUgTSIrKC41K2UpKyIsLjUgTCIrKC41K2UpKyIsMSJ9LEkpLCLilZQiOihIPXt9LEhbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTTEsIisoLjUtdCkrIiBMIisoLjUtZSkrIiwiKyguNS10KSsiIEwiKyguNS1lKSsiLDEgTTEsIisoLjUrdCkrIiBMIisoLjUrZSkrIiwiKyguNSt0KSsiIEwiKyguNStlKSsiLDEifSxIKSwi4pWVIjooaj17fSxqWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLCIrKC41LXQpKyIgTC41LCIrKC41LXQpKyIgTC41LDEgTTAsIisoLjUrdCkrIiBMLjUsIisoLjUrdCl9LGopLCLilZYiOihGPXt9LEZbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTSIrKC41K2UpKyIsMSBMIisoLjUrZSkrIiwuNSBMMCwuNSBNIisoLjUtZSkrIiwuNSBMIisoLjUtZSkrIiwxIn0sRiksIuKVlyI6KFc9e30sV1sxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwiKyguNSt0KSsiIEwiKyguNS1lKSsiLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsMSBNMCwiKyguNS10KSsiIEwiKyguNStlKSsiLCIrKC41LXQpKyIgTCIrKC41K2UpKyIsMSJ9LFcpLCLilZgiOihVPXt9LFVbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTS41LDAgTC41LCIrKC41K3QpKyIgTDEsIisoLjUrdCkrIiBNLjUsIisoLjUtdCkrIiBMMSwiKyguNS10KX0sVSksIuKVmSI6KHE9e30scVsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMSwuNSBMIisoLjUtZSkrIiwuNSBMIisoLjUtZSkrIiwwIE0iKyguNStlKSsiLC41IEwiKyguNStlKSsiLDAifSxxKSwi4pWaIjooTj17fSxOWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0xLCIrKC41LXQpKyIgTCIrKC41K2UpKyIsIisoLjUtdCkrIiBMIisoLjUrZSkrIiwwIE0xLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsIisoLjUrdCkrIiBMIisoLjUtZSkrIiwwIn0sTiksIuKVmyI6KHo9e30selsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwiKyguNSt0KSsiIEwuNSwiKyguNSt0KSsiIEwuNSwwIE0wLCIrKC41LXQpKyIgTC41LCIrKC41LXQpfSx6KSwi4pWcIjooSz17fSxLWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLC41IEwiKyguNStlKSsiLC41IEwiKyguNStlKSsiLDAgTSIrKC41LWUpKyIsLjUgTCIrKC41LWUpKyIsMCJ9LEspLCLilZ0iOihWPXt9LFZbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTTAsIisoLjUtdCkrIiBMIisoLjUtZSkrIiwiKyguNS10KSsiIEwiKyguNS1lKSsiLDAgTTAsIisoLjUrdCkrIiBMIisoLjUrZSkrIiwiKyguNSt0KSsiIEwiKyguNStlKSsiLDAifSxWKSwi4pWeIjooRz17fSxHWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0uNSwwIEwuNSwxIE0uNSwiKyguNS10KSsiIEwxLCIrKC41LXQpKyIgTS41LCIrKC41K3QpKyIgTDEsIisoLjUrdCl9LEcpLCLilZ8iOihZPXt9LFlbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTSIrKC41LWUpKyIsMCBMIisoLjUtZSkrIiwxIE0iKyguNStlKSsiLDAgTCIrKC41K2UpKyIsMSBNIisoLjUrZSkrIiwuNSBMMSwuNSJ9LFkpLCLilaAiOihYPXt9LFhbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTSIrKC41LWUpKyIsMCBMIisoLjUtZSkrIiwxIE0xLCIrKC41K3QpKyIgTCIrKC41K2UpKyIsIisoLjUrdCkrIiBMIisoLjUrZSkrIiwxIE0xLCIrKC41LXQpKyIgTCIrKC41K2UpKyIsIisoLjUtdCkrIiBMIisoLjUrZSkrIiwwIn0sWCksIuKVoSI6KFo9e30sWlsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNLjUsMCBMLjUsMSBNMCwiKyguNS10KSsiIEwuNSwiKyguNS10KSsiIE0wLCIrKC41K3QpKyIgTC41LCIrKC41K3QpfSxaKSwi4pWiIjooSj17fSxKWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLC41IEwiKyguNS1lKSsiLC41IE0iKyguNS1lKSsiLDAgTCIrKC41LWUpKyIsMSBNIisoLjUrZSkrIiwwIEwiKyguNStlKSsiLDEifSxKKSwi4pWjIjooJD17fSwkWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0iKyguNStlKSsiLDAgTCIrKC41K2UpKyIsMSBNMCwiKyguNSt0KSsiIEwiKyguNS1lKSsiLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsMSBNMCwiKyguNS10KSsiIEwiKyguNS1lKSsiLCIrKC41LXQpKyIgTCIrKC41LWUpKyIsMCJ9LCQpLCLilaQiOihRPXt9LFFbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTTAsIisoLjUtdCkrIiBMMSwiKyguNS10KSsiIE0wLCIrKC41K3QpKyIgTDEsIisoLjUrdCkrIiBNLjUsIisoLjUrdCkrIiBMLjUsMSJ9LFEpLCLilaUiOihlZT17fSxlZVsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwuNSBMMSwuNSBNIisoLjUtZSkrIiwuNSBMIisoLjUtZSkrIiwxIE0iKyguNStlKSsiLC41IEwiKyguNStlKSsiLDEifSxlZSksIuKVpiI6KHRlPXt9LHRlWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLCIrKC41LXQpKyIgTDEsIisoLjUtdCkrIiBNMCwiKyguNSt0KSsiIEwiKyguNS1lKSsiLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsMSBNMSwiKyguNSt0KSsiIEwiKyguNStlKSsiLCIrKC41K3QpKyIgTCIrKC41K2UpKyIsMSJ9LHRlKSwi4pWnIjoocmU9e30scmVbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTS41LDAgTC41LCIrKC41LXQpKyIgTTAsIisoLjUtdCkrIiBMMSwiKyguNS10KSsiIE0wLCIrKC41K3QpKyIgTDEsIisoLjUrdCl9LHJlKSwi4pWoIjooaWU9e30saWVbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTTAsLjUgTDEsLjUgTSIrKC41LWUpKyIsLjUgTCIrKC41LWUpKyIsMCBNIisoLjUrZSkrIiwuNSBMIisoLjUrZSkrIiwwIn0saWUpLCLilakiOihuZT17fSxuZVsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwiKyguNSt0KSsiIEwxLCIrKC41K3QpKyIgTTAsIisoLjUtdCkrIiBMIisoLjUtZSkrIiwiKyguNS10KSsiIEwiKyguNS1lKSsiLDAgTTEsIisoLjUtdCkrIiBMIisoLjUrZSkrIiwiKyguNS10KSsiIEwiKyguNStlKSsiLDAifSxuZSksIuKVqiI6KG9lPXt9LG9lWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0uNSwwIEwuNSwxIE0wLCIrKC41LXQpKyIgTDEsIisoLjUtdCkrIiBNMCwiKyguNSt0KSsiIEwxLCIrKC41K3QpfSxvZSksIuKVqyI6KHNlPXt9LHNlWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLC41IEwxLC41IE0iKyguNS1lKSsiLDAgTCIrKC41LWUpKyIsMSBNIisoLjUrZSkrIiwwIEwiKyguNStlKSsiLDEifSxzZSksIuKVrCI6KGFlPXt9LGFlWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsIisoLjUrdCkrIiBMIisoLjUtZSkrIiwxIE0xLCIrKC41K3QpKyIgTCIrKC41K2UpKyIsIisoLjUrdCkrIiBMIisoLjUrZSkrIiwxIE0wLCIrKC41LXQpKyIgTCIrKC41LWUpKyIsIisoLjUtdCkrIiBMIisoLjUtZSkrIiwwIE0xLCIrKC41LXQpKyIgTCIrKC41K2UpKyIsIisoLjUtdCkrIiBMIisoLjUrZSkrIiwwIn0sYWUpLCLilbEiOihjZT17fSxjZVsxXT0iTTEsMCBMMCwxIixjZSksIuKVsiI6KGxlPXt9LGxlWzFdPSJNMCwwIEwxLDEiLGxlKSwi4pWzIjoodWU9e30sdWVbMV09Ik0xLDAgTDAsMSBNMCwwIEwxLDEiLHVlKSwi4pW8IjooaGU9e30saGVbMV09Ik0uNSwuNSBMMCwuNSIsaGVbM109Ik0uNSwuNSBMMSwuNSIsaGUpLCLilb0iOihmZT17fSxmZVsxXT0iTS41LC41IEwuNSwwIixmZVszXT0iTS41LC41IEwuNSwxIixmZSksIuKVviI6KF9lPXt9LF9lWzFdPSJNLjUsLjUgTDEsLjUiLF9lWzNdPSJNLjUsLjUgTDAsLjUiLF9lKSwi4pW/IjooZGU9e30sZGVbMV09Ik0uNSwuNSBMLjUsMSIsZGVbM109Ik0uNSwuNSBMLjUsMCIsZGUpLCLilI0iOihwZT17fSxwZVsxXT0iTS41LC41IEwuNSwxIixwZVszXT0iTS41LC41IEwxLC41IixwZSksIuKUjiI6KHZlPXt9LHZlWzFdPSJNLjUsLjUgTDEsLjUiLHZlWzNdPSJNLjUsLjUgTC41LDEiLHZlKSwi4pSRIjooZ2U9e30sZ2VbMV09Ik0uNSwuNSBMLjUsMSIsZ2VbM109Ik0uNSwuNSBMMCwuNSIsZ2UpLCLilJIiOih5ZT17fSx5ZVsxXT0iTS41LC41IEwwLC41Iix5ZVszXT0iTS41LC41IEwuNSwxIix5ZSksIuKUlSI6KG1lPXt9LG1lWzFdPSJNLjUsLjUgTC41LDAiLG1lWzNdPSJNLjUsLjUgTDEsLjUiLG1lKSwi4pSWIjooYmU9e30sYmVbMV09Ik0uNSwuNSBMMSwuNSIsYmVbM109Ik0uNSwuNSBMLjUsMCIsYmUpLCLilJkiOihTZT17fSxTZVsxXT0iTS41LC41IEwuNSwwIixTZVszXT0iTS41LC41IEwwLC41IixTZSksIuKUmiI6KENlPXt9LENlWzFdPSJNLjUsLjUgTDAsLjUiLENlWzNdPSJNLjUsLjUgTC41LDAiLENlKSwi4pSdIjood2U9e30sd2VbMV09Ik0uNSwwIEwuNSwxIix3ZVszXT0iTS41LC41IEwxLC41Iix3ZSksIuKUniI6KExlPXt9LExlWzFdPSJNMC41LDEgTC41LC41IEwxLC41IixMZVszXT0iTS41LC41IEwuNSwwIixMZSksIuKUnyI6KEVlPXt9LEVlWzFdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLEVlWzNdPSJNLjUsLjUgTC41LDEiLEVlKSwi4pSgIjooeGU9e30seGVbMV09Ik0uNSwuNSBMMSwuNSIseGVbM109Ik0uNSwwIEwuNSwxIix4ZSksIuKUoSI6KEFlPXt9LEFlWzFdPSJNLjUsLjUgTC41LDEiLEFlWzNdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLEFlKSwi4pSiIjooa2U9e30sa2VbMV09Ik0uNSwuNSBMLjUsMCIsa2VbM109Ik0wLjUsMSBMLjUsLjUgTDEsLjUiLGtlKSwi4pSlIjooTWU9e30sTWVbMV09Ik0uNSwwIEwuNSwxIixNZVszXT0iTS41LC41IEwwLC41IixNZSksIuKUpiI6KFJlPXt9LFJlWzFdPSJNMCwuNSBMLjUsLjUgTC41LDEiLFJlWzNdPSJNLjUsLjUgTC41LDAiLFJlKSwi4pSnIjooVGU9e30sVGVbMV09Ik0uNSwwIEwuNSwuNSBMMCwuNSIsVGVbM109Ik0uNSwuNSBMLjUsMSIsVGUpLCLilKgiOihPZT17fSxPZVsxXT0iTS41LC41IEwwLC41IixPZVszXT0iTS41LDAgTC41LDEiLE9lKSwi4pSpIjooQmU9e30sQmVbMV09Ik0uNSwuNSBMLjUsMSIsQmVbM109Ik0uNSwwIEwuNSwuNSBMMCwuNSIsQmUpLCLilKoiOihEZT17fSxEZVsxXT0iTS41LC41IEwuNSwwIixEZVszXT0iTTAsLjUgTC41LC41IEwuNSwxIixEZSksIuKUrSI6KFBlPXt9LFBlWzFdPSJNMC41LDEgTC41LC41IEwxLC41IixQZVszXT0iTS41LC41IEwwLC41IixQZSksIuKUriI6KEllPXt9LEllWzFdPSJNMCwuNSBMLjUsLjUgTC41LDEiLEllWzNdPSJNLjUsLjUgTDEsLjUiLEllKSwi4pSvIjooSGU9e30sSGVbMV09Ik0uNSwuNSBMLjUsMSIsSGVbM109Ik0wLC41IEwxLC41IixIZSksIuKUsCI6KGplPXt9LGplWzFdPSJNMCwuNSBMMSwuNSIsamVbM109Ik0uNSwuNSBMLjUsMSIsamUpLCLilLEiOihGZT17fSxGZVsxXT0iTS41LC41IEwxLC41IixGZVszXT0iTTAsLjUgTC41LC41IEwuNSwxIixGZSksIuKUsiI6KFdlPXt9LFdlWzFdPSJNLjUsLjUgTDAsLjUiLFdlWzNdPSJNMC41LDEgTC41LC41IEwxLC41IixXZSksIuKUtSI6KFVlPXt9LFVlWzFdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLFVlWzNdPSJNLjUsLjUgTDAsLjUiLFVlKSwi4pS2IjoocWU9e30scWVbMV09Ik0uNSwwIEwuNSwuNSBMMCwuNSIscWVbM109Ik0uNSwuNSBMMSwuNSIscWUpLCLilLciOihOZT17fSxOZVsxXT0iTS41LC41IEwuNSwwIixOZVszXT0iTTAsLjUgTDEsLjUiLE5lKSwi4pS4IjooemU9e30semVbMV09Ik0wLC41IEwxLC41Iix6ZVszXT0iTS41LC41IEwuNSwwIix6ZSksIuKUuSI6KEtlPXt9LEtlWzFdPSJNLjUsLjUgTDEsLjUiLEtlWzNdPSJNLjUsMCBMLjUsLjUgTDAsLjUiLEtlKSwi4pS6IjooVmU9e30sVmVbMV09Ik0uNSwuNSBMMCwuNSIsVmVbM109Ik0uNSwwIEwuNSwuNSBMMSwuNSIsVmUpLCLilL0iOihHZT17fSxHZVsxXT0iTS41LDAgTC41LDEgTS41LC41IEwxLC41IixHZVszXT0iTS41LC41IEwwLC41IixHZSksIuKUviI6KFllPXt9LFllWzFdPSJNLjUsMCBMLjUsMSBNLjUsLjUgTDAsLjUiLFllWzNdPSJNLjUsLjUgTDEsLjUiLFllKSwi4pS/IjooWGU9e30sWGVbMV09Ik0uNSwwIEwuNSwxIixYZVszXT0iTTAsLjUgTDEsLjUiLFhlKSwi4pWAIjooWmU9e30sWmVbMV09Ik0wLC41IEwxLC41IE0uNSwuNSBMLjUsMSIsWmVbM109Ik0uNSwuNSBMLjUsMCIsWmUpLCLilYEiOihKZT17fSxKZVsxXT0iTS41LC41IEwuNSwwIE0wLC41IEwxLC41IixKZVszXT0iTS41LC41IEwuNSwxIixKZSksIuKVgiI6KCRlPXt9LCRlWzFdPSJNMCwuNSBMMSwuNSIsJGVbM109Ik0uNSwwIEwuNSwxIiwkZSksIuKVgyI6KFFlPXt9LFFlWzFdPSJNMC41LDEgTC41LC41IEwxLC41IixRZVszXT0iTS41LDAgTC41LC41IEwwLC41IixRZSksIuKVhCI6KGV0PXt9LGV0WzFdPSJNMCwuNSBMLjUsLjUgTC41LDEiLGV0WzNdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLGV0KSwi4pWFIjoodHQ9e30sdHRbMV09Ik0uNSwwIEwuNSwuNSBMMSwuNSIsdHRbM109Ik0wLC41IEwuNSwuNSBMLjUsMSIsdHQpLCLilYYiOihydD17fSxydFsxXT0iTS41LDAgTC41LC41IEwwLC41IixydFszXT0iTTAuNSwxIEwuNSwuNSBMMSwuNSIscnQpLCLilYciOihpdD17fSxpdFsxXT0iTS41LC41IEwuNSwxIixpdFszXT0iTS41LC41IEwuNSwwIE0wLC41IEwxLC41IixpdCksIuKViCI6KG50PXt9LG50WzFdPSJNLjUsLjUgTC41LDAiLG50WzNdPSJNMCwuNSBMMSwuNSBNLjUsLjUgTC41LDEiLG50KSwi4pWJIjoob3Q9e30sb3RbMV09Ik0uNSwuNSBMMSwuNSIsb3RbM109Ik0uNSwwIEwuNSwxIE0uNSwuNSBMMCwuNSIsb3QpLCLilYoiOihzdD17fSxzdFsxXT0iTS41LC41IEwwLC41IixzdFszXT0iTS41LDAgTC41LDEgTS41LC41IEwxLC41IixzdCksIuKVjCI6KGF0PXt9LGF0WzFdPSJNLjEsLjUgTC40LC41IE0uNiwuNSBMLjksLjUiLGF0KSwi4pWNIjooY3Q9e30sY3RbM109Ik0uMSwuNSBMLjQsLjUgTS42LC41IEwuOSwuNSIsY3QpLCLilIQiOihsdD17fSxsdFsxXT0iTS4wNjY3LC41IEwuMjY2NywuNSBNLjQsLjUgTC42LC41IE0uNzMzMywuNSBMLjkzMzMsLjUiLGx0KSwi4pSFIjoodXQ9e30sdXRbM109Ik0uMDY2NywuNSBMLjI2NjcsLjUgTS40LC41IEwuNiwuNSBNLjczMzMsLjUgTC45MzMzLC41Iix1dCksIuKUiCI6KGh0PXt9LGh0WzFdPSJNLjA1LC41IEwuMiwuNSBNLjMsLjUgTC40NSwuNSBNLjU1LC41IEwuNywuNSBNLjgsLjUgTC45NSwuNSIsaHQpLCLilIkiOihmdD17fSxmdFszXT0iTS4wNSwuNSBMLjIsLjUgTS4zLC41IEwuNDUsLjUgTS41NSwuNSBMLjcsLjUgTS44LC41IEwuOTUsLjUiLGZ0KSwi4pWOIjooX3Q9e30sX3RbMV09Ik0uNSwuMSBMLjUsLjQgTS41LC42IEwuNSwuOSIsX3QpLCLilY8iOihkdD17fSxkdFszXT0iTS41LC4xIEwuNSwuNCBNLjUsLjYgTC41LC45IixkdCksIuKUhiI6KHB0PXt9LHB0WzFdPSJNLjUsLjA2NjcgTC41LC4yNjY3IE0uNSwuNCBMLjUsLjYgTS41LC43MzMzIEwuNSwuOTMzMyIscHQpLCLilIciOih2dD17fSx2dFszXT0iTS41LC4wNjY3IEwuNSwuMjY2NyBNLjUsLjQgTC41LC42IE0uNSwuNzMzMyBMLjUsLjkzMzMiLHZ0KSwi4pSKIjooZ3Q9e30sZ3RbMV09Ik0uNSwuMDUgTC41LC4yIE0uNSwuMyBMLjUsLjQ1IEwuNSwuNTUgTS41LC43IEwuNSwuOTUiLGd0KSwi4pSLIjooeXQ9e30seXRbM109Ik0uNSwuMDUgTC41LC4yIE0uNSwuMyBMLjUsLjQ1IEwuNSwuNTUgTS41LC43IEwuNSwuOTUiLHl0KSwi4pWtIjoobXQ9e30sbXRbMV09IkMuNSwxLC41LC41LDEsLjUiLG10KSwi4pWuIjooYnQ9e30sYnRbMV09IkMuNSwxLC41LC41LDAsLjUiLGJ0KSwi4pWvIjooU3Q9e30sU3RbMV09IkMuNSwwLC41LC41LDAsLjUiLFN0KSwi4pWwIjooQ3Q9e30sQ3RbMV09IkMuNSwwLC41LC41LDEsLjUiLEN0KX0sdC50cnlEcmF3Q3VzdG9tQ2hhcj1mdW5jdGlvbihlLHIsaSxuLG8scyl7dmFyIGE9dC5ibG9ja0VsZW1lbnREZWZpbml0aW9uc1tyXTtpZihhKXJldHVybiBmdW5jdGlvbihlLHQscixpLG4sbyl7Zm9yKHZhciBzPTA7czx0Lmxlbmd0aDtzKyspe3ZhciBhPXRbc10sYz1uLzgsbD1vLzg7ZS5maWxsUmVjdChyK2EueCpjLGkrYS55KmwsYS53KmMsYS5oKmwpfX0oZSxhLGksbixvLHMpLCEwO3ZhciBjPUx0W3JdO2lmKGMpcmV0dXJuIGZ1bmN0aW9uKGUsdCxyLGksbixvKXt2YXIgcyxhPUV0LmdldCh0KTthfHwoYT1uZXcgTWFwLEV0LnNldCh0LGEpKTt2YXIgYz1lLmZpbGxTdHlsZTtpZigic3RyaW5nIiE9dHlwZW9mIGMpdGhyb3cgbmV3IEVycm9yKCdVbmV4cGVjdGVkIGZpbGxTdHlsZSB0eXBlICInK2MrJyInKTt2YXIgbD1hLmdldChjKTtpZighbCl7dmFyIHU9dFswXS5sZW5ndGgsaD10Lmxlbmd0aCxmPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImNhbnZhcyIpO2Yud2lkdGg9dSxmLmhlaWdodD1oO3ZhciBfPSgwLHd0LnRocm93SWZGYWxzeSkoZi5nZXRDb250ZXh0KCIyZCIpKSxkPW5ldyBJbWFnZURhdGEodSxoKSxwPXZvaWQgMCx2PXZvaWQgMCxnPXZvaWQgMCx5PXZvaWQgMDtpZihjLnN0YXJ0c1dpdGgoIiMiKSlwPXBhcnNlSW50KGMuc3Vic3RyKDEsMiksMTYpLHY9cGFyc2VJbnQoYy5zdWJzdHIoMywyKSwxNiksZz1wYXJzZUludChjLnN1YnN0cig1LDIpLDE2KSx5PWMubGVuZ3RoPjcmJnBhcnNlSW50KGMuc3Vic3RyKDcsMiksMTYpfHwxO2Vsc2V7aWYoIWMuc3RhcnRzV2l0aCgicmdiYSIpKXRocm93IG5ldyBFcnJvcignVW5leHBlY3RlZCBmaWxsU3R5bGUgY29sb3IgZm9ybWF0ICInK2MrJyIgd2hlbiBkcmF3aW5nIHBhdHRlcm4gZ2x5cGgnKTtwPShzPWMuc3Vic3RyaW5nKDUsYy5sZW5ndGgtMSkuc3BsaXQoIiwiKS5tYXAoKGZ1bmN0aW9uKGUpe3JldHVybiBwYXJzZUZsb2F0KGUpfSkpKVswXSx2PXNbMV0sZz1zWzJdLHk9c1szXX1mb3IodmFyIG09MDttPGg7bSsrKWZvcih2YXIgYj0wO2I8dTtiKyspZC5kYXRhWzQqKG0qdStiKV09cCxkLmRhdGFbNCoobSp1K2IpKzFdPXYsZC5kYXRhWzQqKG0qdStiKSsyXT1nLGQuZGF0YVs0KihtKnUrYikrM109dFttXVtiXSooMjU1KnkpO18ucHV0SW1hZ2VEYXRhKGQsMCwwKSxsPSgwLHd0LnRocm93SWZGYWxzeSkoZS5jcmVhdGVQYXR0ZXJuKGYsbnVsbCkpLGEuc2V0KGMsbCl9ZS5maWxsU3R5bGU9bCxlLmZpbGxSZWN0KHIsaSxuLG8pfShlLGMsaSxuLG8scyksITA7dmFyIGw9dC5ib3hEcmF3aW5nRGVmaW5pdGlvbnNbcl07cmV0dXJuISFsJiYoZnVuY3Rpb24oZSx0LHIsaSxuLG8pe2Uuc3Ryb2tlU3R5bGU9ZS5maWxsU3R5bGU7Zm9yKHZhciBzPTAsYT1PYmplY3QuZW50cmllcyh0KTtzPGEubGVuZ3RoO3MrKyl7dmFyIGM9YVtzXSxsPWNbMF0sdT1jWzFdO2UuYmVnaW5QYXRoKCksZS5saW5lV2lkdGg9d2luZG93LmRldmljZVBpeGVsUmF0aW8qTnVtYmVyLnBhcnNlSW50KGwpO2Zvcih2YXIgaD0wLGY9KCJmdW5jdGlvbiI9PXR5cGVvZiB1P3UoLjE1LC4xNS9vKm4pOnUpLnNwbGl0KCIgIik7aDxmLmxlbmd0aDtoKyspe3ZhciBfPWZbaF0sZD1fWzBdLHA9QXRbZF07aWYocCl7dmFyIHY9Xy5zdWJzdHJpbmcoMSkuc3BsaXQoIiwiKTt2WzBdJiZ2WzFdJiZwKGUsa3QodixuLG8scixpKSl9ZWxzZSBjb25zb2xlLmVycm9yKCdDb3VsZCBub3QgZmluZCBkcmF3aW5nIGluc3RydWN0aW9ucyBmb3IgIicrZCsnIicpfWUuc3Ryb2tlKCksZS5jbG9zZVBhdGgoKX19KGUsbCxpLG4sbyxzKSwhMCl9O3ZhciBFdD1uZXcgTWFwO2Z1bmN0aW9uIHh0KGUsdCxyKXtyZXR1cm4gdm9pZCAwPT09ciYmKHI9MCksTWF0aC5tYXgoTWF0aC5taW4oZSx0KSxyKX12YXIgQXQ9e0M6ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZS5iZXppZXJDdXJ2ZVRvKHRbMF0sdFsxXSx0WzJdLHRbM10sdFs0XSx0WzVdKX0sTDpmdW5jdGlvbihlLHQpe3JldHVybiBlLmxpbmVUbyh0WzBdLHRbMV0pfSxNOmZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUubW92ZVRvKHRbMF0sdFsxXSl9fTtmdW5jdGlvbiBrdChlLHQscixpLG4pe3ZhciBvPWUubWFwKChmdW5jdGlvbihlKXtyZXR1cm4gcGFyc2VGbG9hdChlKXx8cGFyc2VJbnQoZSl9KSk7aWYoby5sZW5ndGg8Mil0aHJvdyBuZXcgRXJyb3IoIlRvbyBmZXcgYXJndW1lbnRzIGZvciBpbnN0cnVjdGlvbiIpO2Zvcih2YXIgcz0wO3M8by5sZW5ndGg7cys9MilvW3NdKj10LDAhPT1vW3NdJiYob1tzXT14dChNYXRoLnJvdW5kKG9bc10rLjUpLS41LHQsMCkpLG9bc10rPWk7Zm9yKHZhciBhPTE7YTxvLmxlbmd0aDthKz0yKW9bYV0qPXIsMCE9PW9bYV0mJihvW2FdPXh0KE1hdGgucm91bmQob1thXSsuNSktLjUsciwwKSksb1thXSs9bjtyZXR1cm4gb319LDM3MDA6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5HcmlkQ2FjaGU9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuY2FjaGU9W119cmV0dXJuIGUucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe2Zvcih2YXIgcj0wO3I8ZTtyKyspe3RoaXMuY2FjaGUubGVuZ3RoPD1yJiZ0aGlzLmNhY2hlLnB1c2goW10pO2Zvcih2YXIgaT10aGlzLmNhY2hlW3JdLmxlbmd0aDtpPHQ7aSsrKXRoaXMuY2FjaGVbcl0ucHVzaCh2b2lkIDApO3RoaXMuY2FjaGVbcl0ubGVuZ3RoPXR9dGhpcy5jYWNoZS5sZW5ndGg9ZX0sZS5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXtmb3IodmFyIGU9MDtlPHRoaXMuY2FjaGUubGVuZ3RoO2UrKylmb3IodmFyIHQ9MDt0PHRoaXMuY2FjaGVbZV0ubGVuZ3RoO3QrKyl0aGlzLmNhY2hlW2VdW3RdPXZvaWQgMH0sZX0oKTt0LkdyaWRDYWNoZT1yfSw1MDk4OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkxpbmtSZW5kZXJMYXllcj12b2lkIDA7dmFyIGE9cigxNTQ2KSxjPXIoODgwMyksbD1yKDIwNDApLHU9cigyNTg1KSxoPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyLGksbixvLHMsYSxjKXt2YXIgbD1lLmNhbGwodGhpcyx0LCJsaW5rIixyLCEwLGksbixhLGMpfHx0aGlzO3JldHVybiBvLm9uU2hvd0xpbmtVbmRlcmxpbmUoKGZ1bmN0aW9uKGUpe3JldHVybiBsLl9vblNob3dMaW5rVW5kZXJsaW5lKGUpfSkpLG8ub25IaWRlTGlua1VuZGVybGluZSgoZnVuY3Rpb24oZSl7cmV0dXJuIGwuX29uSGlkZUxpbmtVbmRlcmxpbmUoZSl9KSkscy5vblNob3dMaW5rVW5kZXJsaW5lKChmdW5jdGlvbihlKXtyZXR1cm4gbC5fb25TaG93TGlua1VuZGVybGluZShlKX0pKSxzLm9uSGlkZUxpbmtVbmRlcmxpbmUoKGZ1bmN0aW9uKGUpe3JldHVybiBsLl9vbkhpZGVMaW5rVW5kZXJsaW5lKGUpfSkpLGx9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5yZXNpemU9ZnVuY3Rpb24odCl7ZS5wcm90b3R5cGUucmVzaXplLmNhbGwodGhpcyx0KSx0aGlzLl9zdGF0ZT12b2lkIDB9LHQucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5fY2xlYXJDdXJyZW50TGluaygpfSx0LnByb3RvdHlwZS5fY2xlYXJDdXJyZW50TGluaz1mdW5jdGlvbigpe2lmKHRoaXMuX3N0YXRlKXt0aGlzLl9jbGVhckNlbGxzKHRoaXMuX3N0YXRlLngxLHRoaXMuX3N0YXRlLnkxLHRoaXMuX3N0YXRlLmNvbHMtdGhpcy5fc3RhdGUueDEsMSk7dmFyIGU9dGhpcy5fc3RhdGUueTItdGhpcy5fc3RhdGUueTEtMTtlPjAmJnRoaXMuX2NsZWFyQ2VsbHMoMCx0aGlzLl9zdGF0ZS55MSsxLHRoaXMuX3N0YXRlLmNvbHMsZSksdGhpcy5fY2xlYXJDZWxscygwLHRoaXMuX3N0YXRlLnkyLHRoaXMuX3N0YXRlLngyLDEpLHRoaXMuX3N0YXRlPXZvaWQgMH19LHQucHJvdG90eXBlLl9vblNob3dMaW5rVW5kZXJsaW5lPWZ1bmN0aW9uKGUpe2lmKGUuZmc9PT1jLklOVkVSVEVEX0RFRkFVTFRfQ09MT1I/dGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuYmFja2dyb3VuZC5jc3M6ZS5mZyYmKDAsbC5pczI1NkNvbG9yKShlLmZnKT90aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5hbnNpW2UuZmddLmNzczp0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5mb3JlZ3JvdW5kLmNzcyxlLnkxPT09ZS55Mil0aGlzLl9maWxsQm90dG9tTGluZUF0Q2VsbHMoZS54MSxlLnkxLGUueDItZS54MSk7ZWxzZXt0aGlzLl9maWxsQm90dG9tTGluZUF0Q2VsbHMoZS54MSxlLnkxLGUuY29scy1lLngxKTtmb3IodmFyIHQ9ZS55MSsxO3Q8ZS55Mjt0KyspdGhpcy5fZmlsbEJvdHRvbUxpbmVBdENlbGxzKDAsdCxlLmNvbHMpO3RoaXMuX2ZpbGxCb3R0b21MaW5lQXRDZWxscygwLGUueTIsZS54Mil9dGhpcy5fc3RhdGU9ZX0sdC5wcm90b3R5cGUuX29uSGlkZUxpbmtVbmRlcmxpbmU9ZnVuY3Rpb24oZSl7dGhpcy5fY2xlYXJDdXJyZW50TGluaygpfSxvKFtzKDYsdS5JQnVmZmVyU2VydmljZSkscyg3LHUuSU9wdGlvbnNTZXJ2aWNlKV0sdCl9KGEuQmFzZVJlbmRlckxheWVyKTt0LkxpbmtSZW5kZXJMYXllcj1ofSwzNTI1OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlJlbmRlcmVyPXZvaWQgMDt2YXIgYT1yKDk1OTYpLGM9cig0MTQ5KSxsPXIoMjUxMiksdT1yKDUwOTgpLGg9cig4NDQpLGY9cig0NzI1KSxfPXIoMjU4NSksZD1yKDE0MjApLHA9cig4NDYwKSx2PTEsZz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpLG4sbyxzLGgsZil7dmFyIF89ZS5jYWxsKHRoaXMpfHx0aGlzO18uX2NvbG9ycz10LF8uX3NjcmVlbkVsZW1lbnQ9cixfLl9idWZmZXJTZXJ2aWNlPXMsXy5fY2hhclNpemVTZXJ2aWNlPWgsXy5fb3B0aW9uc1NlcnZpY2U9ZixfLl9pZD12KyssXy5fb25SZXF1ZXN0UmVkcmF3PW5ldyBwLkV2ZW50RW1pdHRlcjt2YXIgZD1fLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmFsbG93VHJhbnNwYXJlbmN5O3JldHVybiBfLl9yZW5kZXJMYXllcnM9W28uY3JlYXRlSW5zdGFuY2UoYS5UZXh0UmVuZGVyTGF5ZXIsXy5fc2NyZWVuRWxlbWVudCwwLF8uX2NvbG9ycyxkLF8uX2lkKSxvLmNyZWF0ZUluc3RhbmNlKGMuU2VsZWN0aW9uUmVuZGVyTGF5ZXIsXy5fc2NyZWVuRWxlbWVudCwxLF8uX2NvbG9ycyxfLl9pZCksby5jcmVhdGVJbnN0YW5jZSh1LkxpbmtSZW5kZXJMYXllcixfLl9zY3JlZW5FbGVtZW50LDIsXy5fY29sb3JzLF8uX2lkLGksbiksby5jcmVhdGVJbnN0YW5jZShsLkN1cnNvclJlbmRlckxheWVyLF8uX3NjcmVlbkVsZW1lbnQsMyxfLl9jb2xvcnMsXy5faWQsXy5fb25SZXF1ZXN0UmVkcmF3KV0sXy5kaW1lbnNpb25zPXtzY2FsZWRDaGFyV2lkdGg6MCxzY2FsZWRDaGFySGVpZ2h0OjAsc2NhbGVkQ2VsbFdpZHRoOjAsc2NhbGVkQ2VsbEhlaWdodDowLHNjYWxlZENoYXJMZWZ0OjAsc2NhbGVkQ2hhclRvcDowLHNjYWxlZENhbnZhc1dpZHRoOjAsc2NhbGVkQ2FudmFzSGVpZ2h0OjAsY2FudmFzV2lkdGg6MCxjYW52YXNIZWlnaHQ6MCxhY3R1YWxDZWxsV2lkdGg6MCxhY3R1YWxDZWxsSGVpZ2h0OjB9LF8uX2RldmljZVBpeGVsUmF0aW89d2luZG93LmRldmljZVBpeGVsUmF0aW8sXy5fdXBkYXRlRGltZW5zaW9ucygpLF8ub25PcHRpb25zQ2hhbmdlZCgpLF99cmV0dXJuIG4odCxlKSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uUmVxdWVzdFJlZHJhdyIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RSZWRyYXcuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe2Zvcih2YXIgdD0wLHI9dGhpcy5fcmVuZGVyTGF5ZXJzO3Q8ci5sZW5ndGg7dCsrKXJbdF0uZGlzcG9zZSgpO2UucHJvdG90eXBlLmRpc3Bvc2UuY2FsbCh0aGlzKSwoMCxkLnJlbW92ZVRlcm1pbmFsRnJvbUNhY2hlKSh0aGlzLl9pZCl9LHQucHJvdG90eXBlLm9uRGV2aWNlUGl4ZWxSYXRpb0NoYW5nZT1mdW5jdGlvbigpe3RoaXMuX2RldmljZVBpeGVsUmF0aW8hPT13aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyYmKHRoaXMuX2RldmljZVBpeGVsUmF0aW89d2luZG93LmRldmljZVBpeGVsUmF0aW8sdGhpcy5vblJlc2l6ZSh0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKSl9LHQucHJvdG90eXBlLnNldENvbG9ycz1mdW5jdGlvbihlKXt0aGlzLl9jb2xvcnM9ZTtmb3IodmFyIHQ9MCxyPXRoaXMuX3JlbmRlckxheWVyczt0PHIubGVuZ3RoO3QrKyl7dmFyIGk9clt0XTtpLnNldENvbG9ycyh0aGlzLl9jb2xvcnMpLGkucmVzZXQoKX19LHQucHJvdG90eXBlLm9uUmVzaXplPWZ1bmN0aW9uKGUsdCl7dGhpcy5fdXBkYXRlRGltZW5zaW9ucygpO2Zvcih2YXIgcj0wLGk9dGhpcy5fcmVuZGVyTGF5ZXJzO3I8aS5sZW5ndGg7cisrKWlbcl0ucmVzaXplKHRoaXMuZGltZW5zaW9ucyk7dGhpcy5fc2NyZWVuRWxlbWVudC5zdHlsZS53aWR0aD10aGlzLmRpbWVuc2lvbnMuY2FudmFzV2lkdGgrInB4Iix0aGlzLl9zY3JlZW5FbGVtZW50LnN0eWxlLmhlaWdodD10aGlzLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0KyJweCJ9LHQucHJvdG90eXBlLm9uQ2hhclNpemVDaGFuZ2VkPWZ1bmN0aW9uKCl7dGhpcy5vblJlc2l6ZSh0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKX0sdC5wcm90b3R5cGUub25CbHVyPWZ1bmN0aW9uKCl7dGhpcy5fcnVuT3BlcmF0aW9uKChmdW5jdGlvbihlKXtyZXR1cm4gZS5vbkJsdXIoKX0pKX0sdC5wcm90b3R5cGUub25Gb2N1cz1mdW5jdGlvbigpe3RoaXMuX3J1bk9wZXJhdGlvbigoZnVuY3Rpb24oZSl7cmV0dXJuIGUub25Gb2N1cygpfSkpfSx0LnByb3RvdHlwZS5vblNlbGVjdGlvbkNoYW5nZWQ9ZnVuY3Rpb24oZSx0LHIpe3ZvaWQgMD09PXImJihyPSExKSx0aGlzLl9ydW5PcGVyYXRpb24oKGZ1bmN0aW9uKGkpe3JldHVybiBpLm9uU2VsZWN0aW9uQ2hhbmdlZChlLHQscil9KSl9LHQucHJvdG90eXBlLm9uQ3Vyc29yTW92ZT1mdW5jdGlvbigpe3RoaXMuX3J1bk9wZXJhdGlvbigoZnVuY3Rpb24oZSl7cmV0dXJuIGUub25DdXJzb3JNb3ZlKCl9KSl9LHQucHJvdG90eXBlLm9uT3B0aW9uc0NoYW5nZWQ9ZnVuY3Rpb24oKXt0aGlzLl9ydW5PcGVyYXRpb24oKGZ1bmN0aW9uKGUpe3JldHVybiBlLm9uT3B0aW9uc0NoYW5nZWQoKX0pKX0sdC5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXt0aGlzLl9ydW5PcGVyYXRpb24oKGZ1bmN0aW9uKGUpe3JldHVybiBlLnJlc2V0KCl9KSl9LHQucHJvdG90eXBlLl9ydW5PcGVyYXRpb249ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PTAscj10aGlzLl9yZW5kZXJMYXllcnM7dDxyLmxlbmd0aDt0KyspZShyW3RdKX0sdC5wcm90b3R5cGUucmVuZGVyUm93cz1mdW5jdGlvbihlLHQpe2Zvcih2YXIgcj0wLGk9dGhpcy5fcmVuZGVyTGF5ZXJzO3I8aS5sZW5ndGg7cisrKWlbcl0ub25HcmlkQ2hhbmdlZChlLHQpfSx0LnByb3RvdHlwZS5jbGVhclRleHR1cmVBdGxhcz1mdW5jdGlvbigpe2Zvcih2YXIgZT0wLHQ9dGhpcy5fcmVuZGVyTGF5ZXJzO2U8dC5sZW5ndGg7ZSsrKXRbZV0uY2xlYXJUZXh0dXJlQXRsYXMoKX0sdC5wcm90b3R5cGUuX3VwZGF0ZURpbWVuc2lvbnM9ZnVuY3Rpb24oKXt0aGlzLl9jaGFyU2l6ZVNlcnZpY2UuaGFzVmFsaWRTaXplJiYodGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJXaWR0aD1NYXRoLmZsb29yKHRoaXMuX2NoYXJTaXplU2VydmljZS53aWR0aCp3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyksdGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJIZWlnaHQ9TWF0aC5jZWlsKHRoaXMuX2NoYXJTaXplU2VydmljZS5oZWlnaHQqd2luZG93LmRldmljZVBpeGVsUmF0aW8pLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsSGVpZ2h0PU1hdGguZmxvb3IodGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJIZWlnaHQqdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5saW5lSGVpZ2h0KSx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2hhclRvcD0xPT09dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5saW5lSGVpZ2h0PzA6TWF0aC5yb3VuZCgodGhpcy5kaW1lbnNpb25zLnNjYWxlZENlbGxIZWlnaHQtdGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJIZWlnaHQpLzIpLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsV2lkdGg9dGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJXaWR0aCtNYXRoLnJvdW5kKHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMubGV0dGVyU3BhY2luZyksdGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJMZWZ0PU1hdGguZmxvb3IodGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5sZXR0ZXJTcGFjaW5nLzIpLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDYW52YXNIZWlnaHQ9dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKnRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsSGVpZ2h0LHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDYW52YXNXaWR0aD10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMqdGhpcy5kaW1lbnNpb25zLnNjYWxlZENlbGxXaWR0aCx0aGlzLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0PU1hdGgucm91bmQodGhpcy5kaW1lbnNpb25zLnNjYWxlZENhbnZhc0hlaWdodC93aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyksdGhpcy5kaW1lbnNpb25zLmNhbnZhc1dpZHRoPU1hdGgucm91bmQodGhpcy5kaW1lbnNpb25zLnNjYWxlZENhbnZhc1dpZHRoL3dpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKSx0aGlzLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbEhlaWdodD10aGlzLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0L3RoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyx0aGlzLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoPXRoaXMuZGltZW5zaW9ucy5jYW52YXNXaWR0aC90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpfSxvKFtzKDQsXy5JSW5zdGFudGlhdGlvblNlcnZpY2UpLHMoNSxfLklCdWZmZXJTZXJ2aWNlKSxzKDYsZi5JQ2hhclNpemVTZXJ2aWNlKSxzKDcsXy5JT3B0aW9uc1NlcnZpY2UpXSx0KX0oaC5EaXNwb3NhYmxlKTt0LlJlbmRlcmVyPWd9LDE3NTI6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC50aHJvd0lmRmFsc3k9dm9pZCAwLHQudGhyb3dJZkZhbHN5PWZ1bmN0aW9uKGUpe2lmKCFlKXRocm93IG5ldyBFcnJvcigidmFsdWUgbXVzdCBub3QgYmUgZmFsc3kiKTtyZXR1cm4gZX19LDQxNDk6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSksbz10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LHM9dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuU2VsZWN0aW9uUmVuZGVyTGF5ZXI9dm9pZCAwO3ZhciBhPXIoMTU0NiksYz1yKDI1ODUpLGw9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8scyl7dmFyIGE9ZS5jYWxsKHRoaXMsdCwic2VsZWN0aW9uIixyLCEwLGksbixvLHMpfHx0aGlzO3JldHVybiBhLl9jbGVhclN0YXRlKCksYX1yZXR1cm4gbih0LGUpLHQucHJvdG90eXBlLl9jbGVhclN0YXRlPWZ1bmN0aW9uKCl7dGhpcy5fc3RhdGU9e3N0YXJ0OnZvaWQgMCxlbmQ6dm9pZCAwLGNvbHVtblNlbGVjdE1vZGU6dm9pZCAwLHlkaXNwOnZvaWQgMH19LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbih0KXtlLnByb3RvdHlwZS5yZXNpemUuY2FsbCh0aGlzLHQpLHRoaXMuX2NsZWFyU3RhdGUoKX0sdC5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLl9zdGF0ZS5zdGFydCYmdGhpcy5fc3RhdGUuZW5kJiYodGhpcy5fY2xlYXJTdGF0ZSgpLHRoaXMuX2NsZWFyQWxsKCkpfSx0LnByb3RvdHlwZS5vblNlbGVjdGlvbkNoYW5nZWQ9ZnVuY3Rpb24oZSx0LHIpe2lmKHRoaXMuX2RpZFN0YXRlQ2hhbmdlKGUsdCxyLHRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwKSlpZih0aGlzLl9jbGVhckFsbCgpLGUmJnQpe3ZhciBpPWVbMV0tdGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3Asbj10WzFdLXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwLG89TWF0aC5tYXgoaSwwKSxzPU1hdGgubWluKG4sdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEpO2lmKG8+PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93c3x8czwwKXRoaXMuX3N0YXRlLnlkaXNwPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwO2Vsc2V7aWYodGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuc2VsZWN0aW9uVHJhbnNwYXJlbnQuY3NzLHIpe3ZhciBhPWVbMF0sYz10WzBdLWEsbD1zLW8rMTt0aGlzLl9maWxsQ2VsbHMoYSxvLGMsbCl9ZWxzZXthPWk9PT1vP2VbMF06MDt2YXIgdT1vPT09bj90WzBdOnRoaXMuX2J1ZmZlclNlcnZpY2UuY29sczt0aGlzLl9maWxsQ2VsbHMoYSxvLHUtYSwxKTt2YXIgaD1NYXRoLm1heChzLW8tMSwwKTtpZih0aGlzLl9maWxsQ2VsbHMoMCxvKzEsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGgpLG8hPT1zKXt2YXIgZj1uPT09cz90WzBdOnRoaXMuX2J1ZmZlclNlcnZpY2UuY29sczt0aGlzLl9maWxsQ2VsbHMoMCxzLGYsMSl9fXRoaXMuX3N0YXRlLnN0YXJ0PVtlWzBdLGVbMV1dLHRoaXMuX3N0YXRlLmVuZD1bdFswXSx0WzFdXSx0aGlzLl9zdGF0ZS5jb2x1bW5TZWxlY3RNb2RlPXIsdGhpcy5fc3RhdGUueWRpc3A9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3B9fWVsc2UgdGhpcy5fY2xlYXJTdGF0ZSgpfSx0LnByb3RvdHlwZS5fZGlkU3RhdGVDaGFuZ2U9ZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuIXRoaXMuX2FyZUNvb3JkaW5hdGVzRXF1YWwoZSx0aGlzLl9zdGF0ZS5zdGFydCl8fCF0aGlzLl9hcmVDb29yZGluYXRlc0VxdWFsKHQsdGhpcy5fc3RhdGUuZW5kKXx8ciE9PXRoaXMuX3N0YXRlLmNvbHVtblNlbGVjdE1vZGV8fGkhPT10aGlzLl9zdGF0ZS55ZGlzcH0sdC5wcm90b3R5cGUuX2FyZUNvb3JkaW5hdGVzRXF1YWw9ZnVuY3Rpb24oZSx0KXtyZXR1cm4hKCFlfHwhdCkmJmVbMF09PT10WzBdJiZlWzFdPT09dFsxXX0sbyhbcyg0LGMuSUJ1ZmZlclNlcnZpY2UpLHMoNSxjLklPcHRpb25zU2VydmljZSldLHQpfShhLkJhc2VSZW5kZXJMYXllcik7dC5TZWxlY3Rpb25SZW5kZXJMYXllcj1sfSw5NTk2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlRleHRSZW5kZXJMYXllcj12b2lkIDA7dmFyIGE9cigzNzAwKSxjPXIoMTU0NiksbD1yKDM3MzQpLHU9cig2NDMpLGg9cig1MTEpLGY9cigyNTg1KSxfPXIoNDcyNSksZD1yKDQyNjkpLHA9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8scyxjLGwpe3ZhciB1PWUuY2FsbCh0aGlzLHQsInRleHQiLHIsbixpLG8scyxjKXx8dGhpcztyZXR1cm4gdS5fY2hhcmFjdGVySm9pbmVyU2VydmljZT1sLHUuX2NoYXJhY3RlcldpZHRoPTAsdS5fY2hhcmFjdGVyRm9udD0iIix1Ll9jaGFyYWN0ZXJPdmVybGFwQ2FjaGU9e30sdS5fd29ya0NlbGw9bmV3IGguQ2VsbERhdGEsdS5fc3RhdGU9bmV3IGEuR3JpZENhY2hlLHV9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5yZXNpemU9ZnVuY3Rpb24odCl7ZS5wcm90b3R5cGUucmVzaXplLmNhbGwodGhpcyx0KTt2YXIgcj10aGlzLl9nZXRGb250KCExLCExKTt0aGlzLl9jaGFyYWN0ZXJXaWR0aD09PXQuc2NhbGVkQ2hhcldpZHRoJiZ0aGlzLl9jaGFyYWN0ZXJGb250PT09cnx8KHRoaXMuX2NoYXJhY3RlcldpZHRoPXQuc2NhbGVkQ2hhcldpZHRoLHRoaXMuX2NoYXJhY3RlckZvbnQ9cix0aGlzLl9jaGFyYWN0ZXJPdmVybGFwQ2FjaGU9e30pLHRoaXMuX3N0YXRlLmNsZWFyKCksdGhpcy5fc3RhdGUucmVzaXplKHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MpfSx0LnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe3RoaXMuX3N0YXRlLmNsZWFyKCksdGhpcy5fY2xlYXJBbGwoKX0sdC5wcm90b3R5cGUuX2ZvckVhY2hDZWxsPWZ1bmN0aW9uKGUsdCxyKXtmb3IodmFyIGk9ZTtpPD10O2krKylmb3IodmFyIG49aSt0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCxvPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLmxpbmVzLmdldChuKSxzPXRoaXMuX2NoYXJhY3RlckpvaW5lclNlcnZpY2UuZ2V0Sm9pbmVkQ2hhcmFjdGVycyhuKSxhPTA7YTx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM7YSsrKXtvLmxvYWRDZWxsKGEsdGhpcy5fd29ya0NlbGwpO3ZhciBjPXRoaXMuX3dvcmtDZWxsLGw9ITEsaD1hO2lmKDAhPT1jLmdldFdpZHRoKCkpe2lmKHMubGVuZ3RoPjAmJmE9PT1zWzBdWzBdKXtsPSEwO3ZhciBmPXMuc2hpZnQoKTtjPW5ldyBkLkpvaW5lZENlbGxEYXRhKHRoaXMuX3dvcmtDZWxsLG8udHJhbnNsYXRlVG9TdHJpbmcoITAsZlswXSxmWzFdKSxmWzFdLWZbMF0pLGg9ZlsxXS0xfSFsJiZ0aGlzLl9pc092ZXJsYXBwaW5nKGMpJiZoPG8ubGVuZ3RoLTEmJm8uZ2V0Q29kZVBvaW50KGgrMSk9PT11Lk5VTExfQ0VMTF9DT0RFJiYoYy5jb250ZW50Jj0tMTI1ODI5MTMsYy5jb250ZW50fD0yPDwyMikscihjLGEsaSksYT1ofX19LHQucHJvdG90eXBlLl9kcmF3QmFja2dyb3VuZD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMsaT10aGlzLl9jdHgsbj10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsbz0wLHM9MCxhPW51bGw7aS5zYXZlKCksdGhpcy5fZm9yRWFjaENlbGwoZSx0LChmdW5jdGlvbihlLHQsYyl7dmFyIHU9bnVsbDtlLmlzSW52ZXJzZSgpP3U9ZS5pc0ZnRGVmYXVsdCgpP3IuX2NvbG9ycy5mb3JlZ3JvdW5kLmNzczplLmlzRmdSR0IoKT8icmdiKCIrbC5BdHRyaWJ1dGVEYXRhLnRvQ29sb3JSR0IoZS5nZXRGZ0NvbG9yKCkpLmpvaW4oIiwiKSsiKSI6ci5fY29sb3JzLmFuc2lbZS5nZXRGZ0NvbG9yKCldLmNzczplLmlzQmdSR0IoKT91PSJyZ2IoIitsLkF0dHJpYnV0ZURhdGEudG9Db2xvclJHQihlLmdldEJnQ29sb3IoKSkuam9pbigiLCIpKyIpIjplLmlzQmdQYWxldHRlKCkmJih1PXIuX2NvbG9ycy5hbnNpW2UuZ2V0QmdDb2xvcigpXS5jc3MpLG51bGw9PT1hJiYobz10LHM9YyksYyE9PXM/KGkuZmlsbFN0eWxlPWF8fCIiLHIuX2ZpbGxDZWxscyhvLHMsbi1vLDEpLG89dCxzPWMpOmEhPT11JiYoaS5maWxsU3R5bGU9YXx8IiIsci5fZmlsbENlbGxzKG8scyx0LW8sMSksbz10LHM9YyksYT11fSkpLG51bGwhPT1hJiYoaS5maWxsU3R5bGU9YSx0aGlzLl9maWxsQ2VsbHMobyxzLG4tbywxKSksaS5yZXN0b3JlKCl9LHQucHJvdG90eXBlLl9kcmF3Rm9yZWdyb3VuZD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXM7dGhpcy5fZm9yRWFjaENlbGwoZSx0LChmdW5jdGlvbihlLHQsaSl7aWYoIWUuaXNJbnZpc2libGUoKSYmKHIuX2RyYXdDaGFycyhlLHQsaSksZS5pc1VuZGVybGluZSgpfHxlLmlzU3RyaWtldGhyb3VnaCgpKSl7aWYoci5fY3R4LnNhdmUoKSxlLmlzSW52ZXJzZSgpKWlmKGUuaXNCZ0RlZmF1bHQoKSlyLl9jdHguZmlsbFN0eWxlPXIuX2NvbG9ycy5iYWNrZ3JvdW5kLmNzcztlbHNlIGlmKGUuaXNCZ1JHQigpKXIuX2N0eC5maWxsU3R5bGU9InJnYigiK2wuQXR0cmlidXRlRGF0YS50b0NvbG9yUkdCKGUuZ2V0QmdDb2xvcigpKS5qb2luKCIsIikrIikiO2Vsc2V7dmFyIG49ZS5nZXRCZ0NvbG9yKCk7ci5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5kcmF3Qm9sZFRleHRJbkJyaWdodENvbG9ycyYmZS5pc0JvbGQoKSYmbjw4JiYobis9OCksci5fY3R4LmZpbGxTdHlsZT1yLl9jb2xvcnMuYW5zaVtuXS5jc3N9ZWxzZSBpZihlLmlzRmdEZWZhdWx0KCkpci5fY3R4LmZpbGxTdHlsZT1yLl9jb2xvcnMuZm9yZWdyb3VuZC5jc3M7ZWxzZSBpZihlLmlzRmdSR0IoKSlyLl9jdHguZmlsbFN0eWxlPSJyZ2IoIitsLkF0dHJpYnV0ZURhdGEudG9Db2xvclJHQihlLmdldEZnQ29sb3IoKSkuam9pbigiLCIpKyIpIjtlbHNle3ZhciBvPWUuZ2V0RmdDb2xvcigpO3IuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZHJhd0JvbGRUZXh0SW5CcmlnaHRDb2xvcnMmJmUuaXNCb2xkKCkmJm88OCYmKG8rPTgpLHIuX2N0eC5maWxsU3R5bGU9ci5fY29sb3JzLmFuc2lbb10uY3NzfWUuaXNTdHJpa2V0aHJvdWdoKCkmJnIuX2ZpbGxNaWRkbGVMaW5lQXRDZWxscyh0LGksZS5nZXRXaWR0aCgpKSxlLmlzVW5kZXJsaW5lKCkmJnIuX2ZpbGxCb3R0b21MaW5lQXRDZWxscyh0LGksZS5nZXRXaWR0aCgpKSxyLl9jdHgucmVzdG9yZSgpfX0pKX0sdC5wcm90b3R5cGUub25HcmlkQ2hhbmdlZD1mdW5jdGlvbihlLHQpezAhPT10aGlzLl9zdGF0ZS5jYWNoZS5sZW5ndGgmJih0aGlzLl9jaGFyQXRsYXMmJnRoaXMuX2NoYXJBdGxhcy5iZWdpbkZyYW1lKCksdGhpcy5fY2xlYXJDZWxscygwLGUsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLHQtZSsxKSx0aGlzLl9kcmF3QmFja2dyb3VuZChlLHQpLHRoaXMuX2RyYXdGb3JlZ3JvdW5kKGUsdCkpfSx0LnByb3RvdHlwZS5vbk9wdGlvbnNDaGFuZ2VkPWZ1bmN0aW9uKCl7dGhpcy5fc2V0VHJhbnNwYXJlbmN5KHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuYWxsb3dUcmFuc3BhcmVuY3kpfSx0LnByb3RvdHlwZS5faXNPdmVybGFwcGluZz1mdW5jdGlvbihlKXtpZigxIT09ZS5nZXRXaWR0aCgpKXJldHVybiExO2lmKGUuZ2V0Q29kZSgpPDI1NilyZXR1cm4hMTt2YXIgdD1lLmdldENoYXJzKCk7aWYodGhpcy5fY2hhcmFjdGVyT3ZlcmxhcENhY2hlLmhhc093blByb3BlcnR5KHQpKXJldHVybiB0aGlzLl9jaGFyYWN0ZXJPdmVybGFwQ2FjaGVbdF07dGhpcy5fY3R4LnNhdmUoKSx0aGlzLl9jdHguZm9udD10aGlzLl9jaGFyYWN0ZXJGb250O3ZhciByPU1hdGguZmxvb3IodGhpcy5fY3R4Lm1lYXN1cmVUZXh0KHQpLndpZHRoKT50aGlzLl9jaGFyYWN0ZXJXaWR0aDtyZXR1cm4gdGhpcy5fY3R4LnJlc3RvcmUoKSx0aGlzLl9jaGFyYWN0ZXJPdmVybGFwQ2FjaGVbdF09cixyfSxvKFtzKDUsZi5JQnVmZmVyU2VydmljZSkscyg2LGYuSU9wdGlvbnNTZXJ2aWNlKSxzKDcsXy5JQ2hhcmFjdGVySm9pbmVyU2VydmljZSldLHQpfShjLkJhc2VSZW5kZXJMYXllcik7dC5UZXh0UmVuZGVyTGF5ZXI9cH0sOTYxNjooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJhc2VDaGFyQXRsYXM9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX2RpZFdhcm1VcD0hMX1yZXR1cm4gZS5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe30sZS5wcm90b3R5cGUud2FybVVwPWZ1bmN0aW9uKCl7dGhpcy5fZGlkV2FybVVwfHwodGhpcy5fZG9XYXJtVXAoKSx0aGlzLl9kaWRXYXJtVXA9ITApfSxlLnByb3RvdHlwZS5fZG9XYXJtVXA9ZnVuY3Rpb24oKXt9LGUucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7fSxlLnByb3RvdHlwZS5iZWdpbkZyYW1lPWZ1bmN0aW9uKCl7fSxlfSgpO3QuQmFzZUNoYXJBdGxhcz1yfSwxNDIwOihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5yZW1vdmVUZXJtaW5hbEZyb21DYWNoZT10LmFjcXVpcmVDaGFyQXRsYXM9dm9pZCAwO3ZhciBpPXIoMjA0MCksbj1yKDE5MDYpLG89W107dC5hY3F1aXJlQ2hhckF0bGFzPWZ1bmN0aW9uKGUsdCxyLHMsYSl7Zm9yKHZhciBjPSgwLGkuZ2VuZXJhdGVDb25maWcpKHMsYSxlLHIpLGw9MDtsPG8ubGVuZ3RoO2wrKyl7dmFyIHU9KGg9b1tsXSkub3duZWRCeS5pbmRleE9mKHQpO2lmKHU+PTApe2lmKCgwLGkuY29uZmlnRXF1YWxzKShoLmNvbmZpZyxjKSlyZXR1cm4gaC5hdGxhczsxPT09aC5vd25lZEJ5Lmxlbmd0aD8oaC5hdGxhcy5kaXNwb3NlKCksby5zcGxpY2UobCwxKSk6aC5vd25lZEJ5LnNwbGljZSh1LDEpO2JyZWFrfX1mb3IobD0wO2w8by5sZW5ndGg7bCsrKXt2YXIgaD1vW2xdO2lmKCgwLGkuY29uZmlnRXF1YWxzKShoLmNvbmZpZyxjKSlyZXR1cm4gaC5vd25lZEJ5LnB1c2godCksaC5hdGxhc312YXIgZj17YXRsYXM6bmV3IG4uRHluYW1pY0NoYXJBdGxhcyhkb2N1bWVudCxjKSxjb25maWc6Yyxvd25lZEJ5Olt0XX07cmV0dXJuIG8ucHVzaChmKSxmLmF0bGFzfSx0LnJlbW92ZVRlcm1pbmFsRnJvbUNhY2hlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8by5sZW5ndGg7dCsrKXt2YXIgcj1vW3RdLm93bmVkQnkuaW5kZXhPZihlKTtpZigtMSE9PXIpezE9PT1vW3RdLm93bmVkQnkubGVuZ3RoPyhvW3RdLmF0bGFzLmRpc3Bvc2UoKSxvLnNwbGljZSh0LDEpKTpvW3RdLm93bmVkQnkuc3BsaWNlKHIsMSk7YnJlYWt9fX19LDIwNDA6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19zcHJlYWRBcnJheXx8ZnVuY3Rpb24oZSx0LHIpe2lmKHJ8fDI9PT1hcmd1bWVudHMubGVuZ3RoKWZvcih2YXIgaSxuPTAsbz10Lmxlbmd0aDtuPG87bisrKSFpJiZuIGluIHR8fChpfHwoaT1BcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCh0LDAsbikpLGlbbl09dFtuXSk7cmV0dXJuIGUuY29uY2F0KGl8fEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHQpKX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuaXMyNTZDb2xvcj10LmNvbmZpZ0VxdWFscz10LmdlbmVyYXRlQ29uZmlnPXZvaWQgMDt2YXIgbj1yKDY0Myk7dC5nZW5lcmF0ZUNvbmZpZz1mdW5jdGlvbihlLHQscixuKXt2YXIgbz17Zm9yZWdyb3VuZDpuLmZvcmVncm91bmQsYmFja2dyb3VuZDpuLmJhY2tncm91bmQsY3Vyc29yOnZvaWQgMCxjdXJzb3JBY2NlbnQ6dm9pZCAwLHNlbGVjdGlvbjp2b2lkIDAsYW5zaTppKFtdLG4uYW5zaSwhMCl9O3JldHVybntkZXZpY2VQaXhlbFJhdGlvOndpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHNjYWxlZENoYXJXaWR0aDplLHNjYWxlZENoYXJIZWlnaHQ6dCxmb250RmFtaWx5OnIuZm9udEZhbWlseSxmb250U2l6ZTpyLmZvbnRTaXplLGZvbnRXZWlnaHQ6ci5mb250V2VpZ2h0LGZvbnRXZWlnaHRCb2xkOnIuZm9udFdlaWdodEJvbGQsYWxsb3dUcmFuc3BhcmVuY3k6ci5hbGxvd1RyYW5zcGFyZW5jeSxjb2xvcnM6b319LHQuY29uZmlnRXF1YWxzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPTA7cjxlLmNvbG9ycy5hbnNpLmxlbmd0aDtyKyspaWYoZS5jb2xvcnMuYW5zaVtyXS5yZ2JhIT09dC5jb2xvcnMuYW5zaVtyXS5yZ2JhKXJldHVybiExO3JldHVybiBlLmRldmljZVBpeGVsUmF0aW89PT10LmRldmljZVBpeGVsUmF0aW8mJmUuZm9udEZhbWlseT09PXQuZm9udEZhbWlseSYmZS5mb250U2l6ZT09PXQuZm9udFNpemUmJmUuZm9udFdlaWdodD09PXQuZm9udFdlaWdodCYmZS5mb250V2VpZ2h0Qm9sZD09PXQuZm9udFdlaWdodEJvbGQmJmUuYWxsb3dUcmFuc3BhcmVuY3k9PT10LmFsbG93VHJhbnNwYXJlbmN5JiZlLnNjYWxlZENoYXJXaWR0aD09PXQuc2NhbGVkQ2hhcldpZHRoJiZlLnNjYWxlZENoYXJIZWlnaHQ9PT10LnNjYWxlZENoYXJIZWlnaHQmJmUuY29sb3JzLmZvcmVncm91bmQ9PT10LmNvbG9ycy5mb3JlZ3JvdW5kJiZlLmNvbG9ycy5iYWNrZ3JvdW5kPT09dC5jb2xvcnMuYmFja2dyb3VuZH0sdC5pczI1NkNvbG9yPWZ1bmN0aW9uKGUpe3JldHVybiBlPG4uREVGQVVMVF9DT0xPUn19LDg4MDM6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkNIQVJfQVRMQVNfQ0VMTF9TUEFDSU5HPXQuVEVYVF9CQVNFTElORT10LkRJTV9PUEFDSVRZPXQuSU5WRVJURURfREVGQVVMVF9DT0xPUj12b2lkIDA7dmFyIGk9cig2MTE0KTt0LklOVkVSVEVEX0RFRkFVTFRfQ09MT1I9MjU3LHQuRElNX09QQUNJVFk9LjUsdC5URVhUX0JBU0VMSU5FPWkuaXNGaXJlZm94PyJib3R0b20iOiJpZGVvZ3JhcGhpYyIsdC5DSEFSX0FUTEFTX0NFTExfU1BBQ0lORz0xfSwxOTA2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0Lk5vbmVDaGFyQXRsYXM9dC5EeW5hbWljQ2hhckF0bGFzPXQuZ2V0R2x5cGhDYWNoZUtleT12b2lkIDA7dmFyIG89cig4ODAzKSxzPXIoOTYxNiksYT1yKDU2ODApLGM9cig3MDAxKSxsPXIoNjExNCksdT1yKDE3NTIpLGg9cig0Nzc0KSxmPTEwMjQsXz0xMDI0LGQ9e2NzczoicmdiYSgwLCAwLCAwLCAwKSIscmdiYTowfTtmdW5jdGlvbiBwKGUpe3JldHVybiBlLmNvZGU8PDIxfGUuYmc8PDEyfGUuZmc8PDN8KGUuYm9sZD8wOjQpKyhlLmRpbT8wOjIpKyhlLml0YWxpYz8wOjEpfXQuZ2V0R2x5cGhDYWNoZUtleT1wO3ZhciB2PWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyKXt2YXIgaT1lLmNhbGwodGhpcyl8fHRoaXM7aS5fY29uZmlnPXIsaS5fZHJhd1RvQ2FjaGVDb3VudD0wLGkuX2dseXBoc1dhaXRpbmdPbkJpdG1hcD1bXSxpLl9iaXRtYXBDb21taXRUaW1lb3V0PW51bGwsaS5fYml0bWFwPW51bGwsaS5fY2FjaGVDYW52YXM9dC5jcmVhdGVFbGVtZW50KCJjYW52YXMiKSxpLl9jYWNoZUNhbnZhcy53aWR0aD1mLGkuX2NhY2hlQ2FudmFzLmhlaWdodD1fLGkuX2NhY2hlQ3R4PSgwLHUudGhyb3dJZkZhbHN5KShpLl9jYWNoZUNhbnZhcy5nZXRDb250ZXh0KCIyZCIse2FscGhhOiEwfSkpO3ZhciBuPXQuY3JlYXRlRWxlbWVudCgiY2FudmFzIik7bi53aWR0aD1pLl9jb25maWcuc2NhbGVkQ2hhcldpZHRoLG4uaGVpZ2h0PWkuX2NvbmZpZy5zY2FsZWRDaGFySGVpZ2h0LGkuX3RtcEN0eD0oMCx1LnRocm93SWZGYWxzeSkobi5nZXRDb250ZXh0KCIyZCIse2FscGhhOmkuX2NvbmZpZy5hbGxvd1RyYW5zcGFyZW5jeX0pKSxpLl93aWR0aD1NYXRoLmZsb29yKGYvaS5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCksaS5faGVpZ2h0PU1hdGguZmxvb3IoXy9pLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCk7dmFyIG89aS5fd2lkdGgqaS5faGVpZ2h0O3JldHVybiBpLl9jYWNoZU1hcD1uZXcgYy5MUlVNYXAobyksaS5fY2FjaGVNYXAucHJlYWxsb2MobyksaX1yZXR1cm4gbih0LGUpLHQucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXtudWxsIT09dGhpcy5fYml0bWFwQ29tbWl0VGltZW91dCYmKHdpbmRvdy5jbGVhclRpbWVvdXQodGhpcy5fYml0bWFwQ29tbWl0VGltZW91dCksdGhpcy5fYml0bWFwQ29tbWl0VGltZW91dD1udWxsKX0sdC5wcm90b3R5cGUuYmVnaW5GcmFtZT1mdW5jdGlvbigpe3RoaXMuX2RyYXdUb0NhY2hlQ291bnQ9MH0sdC5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXtpZih0aGlzLl9jYWNoZU1hcC5zaXplPjApe3ZhciBlPXRoaXMuX3dpZHRoKnRoaXMuX2hlaWdodDt0aGlzLl9jYWNoZU1hcD1uZXcgYy5MUlVNYXAoZSksdGhpcy5fY2FjaGVNYXAucHJlYWxsb2MoZSl9dGhpcy5fY2FjaGVDdHguY2xlYXJSZWN0KDAsMCxmLF8pLHRoaXMuX3RtcEN0eC5jbGVhclJlY3QoMCwwLHRoaXMuX2NvbmZpZy5zY2FsZWRDaGFyV2lkdGgsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJIZWlnaHQpfSx0LnByb3RvdHlwZS5kcmF3PWZ1bmN0aW9uKGUsdCxyLGkpe2lmKDMyPT09dC5jb2RlKXJldHVybiEwO2lmKCF0aGlzLl9jYW5DYWNoZSh0KSlyZXR1cm4hMTt2YXIgbj1wKHQpLG89dGhpcy5fY2FjaGVNYXAuZ2V0KG4pO2lmKG51bGwhPW8pcmV0dXJuIHRoaXMuX2RyYXdGcm9tQ2FjaGUoZSxvLHIsaSksITA7aWYodGhpcy5fZHJhd1RvQ2FjaGVDb3VudDwxMDApe3ZhciBzO3M9dGhpcy5fY2FjaGVNYXAuc2l6ZTx0aGlzLl9jYWNoZU1hcC5jYXBhY2l0eT90aGlzLl9jYWNoZU1hcC5zaXplOnRoaXMuX2NhY2hlTWFwLnBlZWsoKS5pbmRleDt2YXIgYT10aGlzLl9kcmF3VG9DYWNoZSh0LHMpO3JldHVybiB0aGlzLl9jYWNoZU1hcC5zZXQobixhKSx0aGlzLl9kcmF3RnJvbUNhY2hlKGUsYSxyLGkpLCEwfXJldHVybiExfSx0LnByb3RvdHlwZS5fY2FuQ2FjaGU9ZnVuY3Rpb24oZSl7cmV0dXJuIGUuY29kZTwyNTZ9LHQucHJvdG90eXBlLl90b0Nvb3JkaW5hdGVYPWZ1bmN0aW9uKGUpe3JldHVybiBlJXRoaXMuX3dpZHRoKnRoaXMuX2NvbmZpZy5zY2FsZWRDaGFyV2lkdGh9LHQucHJvdG90eXBlLl90b0Nvb3JkaW5hdGVZPWZ1bmN0aW9uKGUpe3JldHVybiBNYXRoLmZsb29yKGUvdGhpcy5fd2lkdGgpKnRoaXMuX2NvbmZpZy5zY2FsZWRDaGFySGVpZ2h0fSx0LnByb3RvdHlwZS5fZHJhd0Zyb21DYWNoZT1mdW5jdGlvbihlLHQscixpKXtpZighdC5pc0VtcHR5KXt2YXIgbj10aGlzLl90b0Nvb3JkaW5hdGVYKHQuaW5kZXgpLG89dGhpcy5fdG9Db29yZGluYXRlWSh0LmluZGV4KTtlLmRyYXdJbWFnZSh0LmluQml0bWFwP3RoaXMuX2JpdG1hcDp0aGlzLl9jYWNoZUNhbnZhcyxuLG8sdGhpcy5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCx0aGlzLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCxyLGksdGhpcy5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCx0aGlzLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCl9fSx0LnByb3RvdHlwZS5fZ2V0Q29sb3JGcm9tQW5zaUluZGV4PWZ1bmN0aW9uKGUpe3JldHVybiBlPHRoaXMuX2NvbmZpZy5jb2xvcnMuYW5zaS5sZW5ndGg/dGhpcy5fY29uZmlnLmNvbG9ycy5hbnNpW2VdOmEuREVGQVVMVF9BTlNJX0NPTE9SU1tlXX0sdC5wcm90b3R5cGUuX2dldEJhY2tncm91bmRDb2xvcj1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fY29uZmlnLmFsbG93VHJhbnNwYXJlbmN5P2Q6ZS5iZz09PW8uSU5WRVJURURfREVGQVVMVF9DT0xPUj90aGlzLl9jb25maWcuY29sb3JzLmZvcmVncm91bmQ6ZS5iZzwyNTY/dGhpcy5fZ2V0Q29sb3JGcm9tQW5zaUluZGV4KGUuYmcpOnRoaXMuX2NvbmZpZy5jb2xvcnMuYmFja2dyb3VuZH0sdC5wcm90b3R5cGUuX2dldEZvcmVncm91bmRDb2xvcj1mdW5jdGlvbihlKXtyZXR1cm4gZS5mZz09PW8uSU5WRVJURURfREVGQVVMVF9DT0xPUj9oLmNvbG9yLm9wYXF1ZSh0aGlzLl9jb25maWcuY29sb3JzLmJhY2tncm91bmQpOmUuZmc8MjU2P3RoaXMuX2dldENvbG9yRnJvbUFuc2lJbmRleChlLmZnKTp0aGlzLl9jb25maWcuY29sb3JzLmZvcmVncm91bmR9LHQucHJvdG90eXBlLl9kcmF3VG9DYWNoZT1mdW5jdGlvbihlLHQpe3RoaXMuX2RyYXdUb0NhY2hlQ291bnQrKyx0aGlzLl90bXBDdHguc2F2ZSgpO3ZhciByPXRoaXMuX2dldEJhY2tncm91bmRDb2xvcihlKTt0aGlzLl90bXBDdHguZ2xvYmFsQ29tcG9zaXRlT3BlcmF0aW9uPSJjb3B5Iix0aGlzLl90bXBDdHguZmlsbFN0eWxlPXIuY3NzLHRoaXMuX3RtcEN0eC5maWxsUmVjdCgwLDAsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCx0aGlzLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCksdGhpcy5fdG1wQ3R4Lmdsb2JhbENvbXBvc2l0ZU9wZXJhdGlvbj0ic291cmNlLW92ZXIiO3ZhciBpPWUuYm9sZD90aGlzLl9jb25maWcuZm9udFdlaWdodEJvbGQ6dGhpcy5fY29uZmlnLmZvbnRXZWlnaHQsbj1lLml0YWxpYz8iaXRhbGljIjoiIjt0aGlzLl90bXBDdHguZm9udD1uKyIgIitpKyIgIit0aGlzLl9jb25maWcuZm9udFNpemUqdGhpcy5fY29uZmlnLmRldmljZVBpeGVsUmF0aW8rInB4ICIrdGhpcy5fY29uZmlnLmZvbnRGYW1pbHksdGhpcy5fdG1wQ3R4LnRleHRCYXNlbGluZT1vLlRFWFRfQkFTRUxJTkUsdGhpcy5fdG1wQ3R4LmZpbGxTdHlsZT10aGlzLl9nZXRGb3JlZ3JvdW5kQ29sb3IoZSkuY3NzLGUuZGltJiYodGhpcy5fdG1wQ3R4Lmdsb2JhbEFscGhhPW8uRElNX09QQUNJVFkpLHRoaXMuX3RtcEN0eC5maWxsVGV4dChlLmNoYXJzLDAsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJIZWlnaHQpO3ZhciBzPXRoaXMuX3RtcEN0eC5nZXRJbWFnZURhdGEoMCwwLHRoaXMuX2NvbmZpZy5zY2FsZWRDaGFyV2lkdGgsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJIZWlnaHQpLGE9ITE7aWYodGhpcy5fY29uZmlnLmFsbG93VHJhbnNwYXJlbmN5fHwoYT15KHMscikpLGEmJiJfIj09PWUuY2hhcnMmJiF0aGlzLl9jb25maWcuYWxsb3dUcmFuc3BhcmVuY3kpZm9yKHZhciBjPTE7Yzw9NSYmKHRoaXMuX3RtcEN0eC5maWxsVGV4dChlLmNoYXJzLDAsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJIZWlnaHQtYyksYT15KHM9dGhpcy5fdG1wQ3R4LmdldEltYWdlRGF0YSgwLDAsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCx0aGlzLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCkscikpO2MrKyk7dGhpcy5fdG1wQ3R4LnJlc3RvcmUoKTt2YXIgbD10aGlzLl90b0Nvb3JkaW5hdGVYKHQpLHU9dGhpcy5fdG9Db29yZGluYXRlWSh0KTt0aGlzLl9jYWNoZUN0eC5wdXRJbWFnZURhdGEocyxsLHUpO3ZhciBoPXtpbmRleDp0LGlzRW1wdHk6YSxpbkJpdG1hcDohMX07cmV0dXJuIHRoaXMuX2FkZEdseXBoVG9CaXRtYXAoaCksaH0sdC5wcm90b3R5cGUuX2FkZEdseXBoVG9CaXRtYXA9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpczshKCJjcmVhdGVJbWFnZUJpdG1hcCJpbiB3aW5kb3cpfHxsLmlzRmlyZWZveHx8bC5pc1NhZmFyaXx8KHRoaXMuX2dseXBoc1dhaXRpbmdPbkJpdG1hcC5wdXNoKGUpLG51bGw9PT10aGlzLl9iaXRtYXBDb21taXRUaW1lb3V0JiYodGhpcy5fYml0bWFwQ29tbWl0VGltZW91dD13aW5kb3cuc2V0VGltZW91dCgoZnVuY3Rpb24oKXtyZXR1cm4gdC5fZ2VuZXJhdGVCaXRtYXAoKX0pLDEwMCkpKX0sdC5wcm90b3R5cGUuX2dlbmVyYXRlQml0bWFwPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcyx0PXRoaXMuX2dseXBoc1dhaXRpbmdPbkJpdG1hcDt0aGlzLl9nbHlwaHNXYWl0aW5nT25CaXRtYXA9W10sd2luZG93LmNyZWF0ZUltYWdlQml0bWFwKHRoaXMuX2NhY2hlQ2FudmFzKS50aGVuKChmdW5jdGlvbihyKXtlLl9iaXRtYXA9cjtmb3IodmFyIGk9MDtpPHQubGVuZ3RoO2krKyl0W2ldLmluQml0bWFwPSEwfSkpLHRoaXMuX2JpdG1hcENvbW1pdFRpbWVvdXQ9bnVsbH0sdH0ocy5CYXNlQ2hhckF0bGFzKTt0LkR5bmFtaWNDaGFyQXRsYXM9djt2YXIgZz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscil7cmV0dXJuIGUuY2FsbCh0aGlzKXx8dGhpc31yZXR1cm4gbih0LGUpLHQucHJvdG90eXBlLmRyYXc9ZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuITF9LHR9KHMuQmFzZUNoYXJBdGxhcyk7ZnVuY3Rpb24geShlLHQpe2Zvcih2YXIgcj0hMCxpPXQucmdiYT4+PjI0LG49dC5yZ2JhPj4+MTYmMjU1LG89dC5yZ2JhPj4+OCYyNTUscz0wO3M8ZS5kYXRhLmxlbmd0aDtzKz00KWUuZGF0YVtzXT09PWkmJmUuZGF0YVtzKzFdPT09biYmZS5kYXRhW3MrMl09PT1vP2UuZGF0YVtzKzNdPTA6cj0hMTtyZXR1cm4gcn10Lk5vbmVDaGFyQXRsYXM9Z30sNzAwMTooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkxSVU1hcD12b2lkIDA7dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe3RoaXMuY2FwYWNpdHk9ZSx0aGlzLl9tYXA9e30sdGhpcy5faGVhZD1udWxsLHRoaXMuX3RhaWw9bnVsbCx0aGlzLl9ub2RlUG9vbD1bXSx0aGlzLnNpemU9MH1yZXR1cm4gZS5wcm90b3R5cGUuX3VubGlua05vZGU9ZnVuY3Rpb24oZSl7dmFyIHQ9ZS5wcmV2LHI9ZS5uZXh0O2U9PT10aGlzLl9oZWFkJiYodGhpcy5faGVhZD1yKSxlPT09dGhpcy5fdGFpbCYmKHRoaXMuX3RhaWw9dCksbnVsbCE9PXQmJih0Lm5leHQ9ciksbnVsbCE9PXImJihyLnByZXY9dCl9LGUucHJvdG90eXBlLl9hcHBlbmROb2RlPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMuX3RhaWw7bnVsbCE9PXQmJih0Lm5leHQ9ZSksZS5wcmV2PXQsZS5uZXh0PW51bGwsdGhpcy5fdGFpbD1lLG51bGw9PT10aGlzLl9oZWFkJiYodGhpcy5faGVhZD1lKX0sZS5wcm90b3R5cGUucHJlYWxsb2M9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PXRoaXMuX25vZGVQb29sLHI9MDtyPGU7cisrKXQucHVzaCh7cHJldjpudWxsLG5leHQ6bnVsbCxrZXk6bnVsbCx2YWx1ZTpudWxsfSl9LGUucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9tYXBbZV07cmV0dXJuIHZvaWQgMCE9PXQ/KHRoaXMuX3VubGlua05vZGUodCksdGhpcy5fYXBwZW5kTm9kZSh0KSx0LnZhbHVlKTpudWxsfSxlLnByb3RvdHlwZS5wZWVrVmFsdWU9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fbWFwW2VdO3JldHVybiB2b2lkIDAhPT10P3QudmFsdWU6bnVsbH0sZS5wcm90b3R5cGUucGVlaz1mdW5jdGlvbigpe3ZhciBlPXRoaXMuX2hlYWQ7cmV0dXJuIG51bGw9PT1lP251bGw6ZS52YWx1ZX0sZS5wcm90b3R5cGUuc2V0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5fbWFwW2VdO2lmKHZvaWQgMCE9PXIpcj10aGlzLl9tYXBbZV0sdGhpcy5fdW5saW5rTm9kZShyKSxyLnZhbHVlPXQ7ZWxzZSBpZih0aGlzLnNpemU+PXRoaXMuY2FwYWNpdHkpcj10aGlzLl9oZWFkLHRoaXMuX3VubGlua05vZGUociksZGVsZXRlIHRoaXMuX21hcFtyLmtleV0sci5rZXk9ZSxyLnZhbHVlPXQsdGhpcy5fbWFwW2VdPXI7ZWxzZXt2YXIgaT10aGlzLl9ub2RlUG9vbDtpLmxlbmd0aD4wPygocj1pLnBvcCgpKS5rZXk9ZSxyLnZhbHVlPXQpOnI9e3ByZXY6bnVsbCxuZXh0Om51bGwsa2V5OmUsdmFsdWU6dH0sdGhpcy5fbWFwW2VdPXIsdGhpcy5zaXplKyt9dGhpcy5fYXBwZW5kTm9kZShyKX0sZX0oKTt0LkxSVU1hcD1yfSwxMjk2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkRvbVJlbmRlcmVyPXZvaWQgMDt2YXIgYT1yKDM3ODcpLGM9cig4ODAzKSxsPXIoODQ0KSx1PXIoNDcyNSksaD1yKDI1ODUpLGY9cig4NDYwKSxfPXIoNDc3NCksZD1yKDk2MzEpLHA9Inh0ZXJtLWRvbS1yZW5kZXJlci1vd25lci0iLHY9Inh0ZXJtLWZnLSIsZz0ieHRlcm0tYmctIix5PSJ4dGVybS1mb2N1cyIsbT0xLGI9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8scyxjLGwsdSxoKXt2YXIgZj1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIGYuX2NvbG9ycz10LGYuX2VsZW1lbnQ9cixmLl9zY3JlZW5FbGVtZW50PWksZi5fdmlld3BvcnRFbGVtZW50PW4sZi5fbGlua2lmaWVyPW8sZi5fbGlua2lmaWVyMj1zLGYuX2NoYXJTaXplU2VydmljZT1sLGYuX29wdGlvbnNTZXJ2aWNlPXUsZi5fYnVmZmVyU2VydmljZT1oLGYuX3Rlcm1pbmFsQ2xhc3M9bSsrLGYuX3Jvd0VsZW1lbnRzPVtdLGYuX3Jvd0NvbnRhaW5lcj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKSxmLl9yb3dDb250YWluZXIuY2xhc3NMaXN0LmFkZCgieHRlcm0tcm93cyIpLGYuX3Jvd0NvbnRhaW5lci5zdHlsZS5saW5lSGVpZ2h0PSJub3JtYWwiLGYuX3Jvd0NvbnRhaW5lci5zZXRBdHRyaWJ1dGUoImFyaWEtaGlkZGVuIiwidHJ1ZSIpLGYuX3JlZnJlc2hSb3dFbGVtZW50cyhmLl9idWZmZXJTZXJ2aWNlLmNvbHMsZi5fYnVmZmVyU2VydmljZS5yb3dzKSxmLl9zZWxlY3Rpb25Db250YWluZXI9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2IiksZi5fc2VsZWN0aW9uQ29udGFpbmVyLmNsYXNzTGlzdC5hZGQoInh0ZXJtLXNlbGVjdGlvbiIpLGYuX3NlbGVjdGlvbkNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoImFyaWEtaGlkZGVuIiwidHJ1ZSIpLGYuZGltZW5zaW9ucz17c2NhbGVkQ2hhcldpZHRoOjAsc2NhbGVkQ2hhckhlaWdodDowLHNjYWxlZENlbGxXaWR0aDowLHNjYWxlZENlbGxIZWlnaHQ6MCxzY2FsZWRDaGFyTGVmdDowLHNjYWxlZENoYXJUb3A6MCxzY2FsZWRDYW52YXNXaWR0aDowLHNjYWxlZENhbnZhc0hlaWdodDowLGNhbnZhc1dpZHRoOjAsY2FudmFzSGVpZ2h0OjAsYWN0dWFsQ2VsbFdpZHRoOjAsYWN0dWFsQ2VsbEhlaWdodDowfSxmLl91cGRhdGVEaW1lbnNpb25zKCksZi5faW5qZWN0Q3NzKCksZi5fcm93RmFjdG9yeT1jLmNyZWF0ZUluc3RhbmNlKGEuRG9tUmVuZGVyZXJSb3dGYWN0b3J5LGRvY3VtZW50LGYuX2NvbG9ycyksZi5fZWxlbWVudC5jbGFzc0xpc3QuYWRkKHArZi5fdGVybWluYWxDbGFzcyksZi5fc2NyZWVuRWxlbWVudC5hcHBlbmRDaGlsZChmLl9yb3dDb250YWluZXIpLGYuX3NjcmVlbkVsZW1lbnQuYXBwZW5kQ2hpbGQoZi5fc2VsZWN0aW9uQ29udGFpbmVyKSxmLl9saW5raWZpZXIub25TaG93TGlua1VuZGVybGluZSgoZnVuY3Rpb24oZSl7cmV0dXJuIGYuX29uTGlua0hvdmVyKGUpfSkpLGYuX2xpbmtpZmllci5vbkhpZGVMaW5rVW5kZXJsaW5lKChmdW5jdGlvbihlKXtyZXR1cm4gZi5fb25MaW5rTGVhdmUoZSl9KSksZi5fbGlua2lmaWVyMi5vblNob3dMaW5rVW5kZXJsaW5lKChmdW5jdGlvbihlKXtyZXR1cm4gZi5fb25MaW5rSG92ZXIoZSl9KSksZi5fbGlua2lmaWVyMi5vbkhpZGVMaW5rVW5kZXJsaW5lKChmdW5jdGlvbihlKXtyZXR1cm4gZi5fb25MaW5rTGVhdmUoZSl9KSksZn1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0UmVkcmF3Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuKG5ldyBmLkV2ZW50RW1pdHRlcikuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3RoaXMuX2VsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZShwK3RoaXMuX3Rlcm1pbmFsQ2xhc3MpLCgwLGQucmVtb3ZlRWxlbWVudEZyb21QYXJlbnQpKHRoaXMuX3Jvd0NvbnRhaW5lcix0aGlzLl9zZWxlY3Rpb25Db250YWluZXIsdGhpcy5fdGhlbWVTdHlsZUVsZW1lbnQsdGhpcy5fZGltZW5zaW9uc1N0eWxlRWxlbWVudCksZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpfSx0LnByb3RvdHlwZS5fdXBkYXRlRGltZW5zaW9ucz1mdW5jdGlvbigpe3RoaXMuZGltZW5zaW9ucy5zY2FsZWRDaGFyV2lkdGg9dGhpcy5fY2hhclNpemVTZXJ2aWNlLndpZHRoKndpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDaGFySGVpZ2h0PU1hdGguY2VpbCh0aGlzLl9jaGFyU2l6ZVNlcnZpY2UuaGVpZ2h0KndpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKSx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2VsbFdpZHRoPXRoaXMuZGltZW5zaW9ucy5zY2FsZWRDaGFyV2lkdGgrTWF0aC5yb3VuZCh0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmxldHRlclNwYWNpbmcpLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsSGVpZ2h0PU1hdGguZmxvb3IodGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJIZWlnaHQqdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5saW5lSGVpZ2h0KSx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2hhckxlZnQ9MCx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2hhclRvcD0wLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDYW52YXNXaWR0aD10aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2VsbFdpZHRoKnRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2FudmFzSGVpZ2h0PXRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsSGVpZ2h0KnRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyx0aGlzLmRpbWVuc2lvbnMuY2FudmFzV2lkdGg9TWF0aC5yb3VuZCh0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2FudmFzV2lkdGgvd2luZG93LmRldmljZVBpeGVsUmF0aW8pLHRoaXMuZGltZW5zaW9ucy5jYW52YXNIZWlnaHQ9TWF0aC5yb3VuZCh0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2FudmFzSGVpZ2h0L3dpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKSx0aGlzLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoPXRoaXMuZGltZW5zaW9ucy5jYW52YXNXaWR0aC90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5kaW1lbnNpb25zLmFjdHVhbENlbGxIZWlnaHQ9dGhpcy5kaW1lbnNpb25zLmNhbnZhc0hlaWdodC90aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3M7Zm9yKHZhciBlPTAsdD10aGlzLl9yb3dFbGVtZW50cztlPHQubGVuZ3RoO2UrKyl7dmFyIHI9dFtlXTtyLnN0eWxlLndpZHRoPXRoaXMuZGltZW5zaW9ucy5jYW52YXNXaWR0aCsicHgiLHIuc3R5bGUuaGVpZ2h0PXRoaXMuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KyJweCIsci5zdHlsZS5saW5lSGVpZ2h0PXRoaXMuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KyJweCIsci5zdHlsZS5vdmVyZmxvdz0iaGlkZGVuIn10aGlzLl9kaW1lbnNpb25zU3R5bGVFbGVtZW50fHwodGhpcy5fZGltZW5zaW9uc1N0eWxlRWxlbWVudD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzdHlsZSIpLHRoaXMuX3NjcmVlbkVsZW1lbnQuYXBwZW5kQ2hpbGQodGhpcy5fZGltZW5zaW9uc1N0eWxlRWxlbWVudCkpO3ZhciBpPXRoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cyBzcGFuIHsgZGlzcGxheTogaW5saW5lLWJsb2NrOyBoZWlnaHQ6IDEwMCU7IHZlcnRpY2FsLWFsaWduOiB0b3A7IHdpZHRoOiAiK3RoaXMuZGltZW5zaW9ucy5hY3R1YWxDZWxsV2lkdGgrInB4fSI7dGhpcy5fZGltZW5zaW9uc1N0eWxlRWxlbWVudC50ZXh0Q29udGVudD1pLHRoaXMuX3NlbGVjdGlvbkNvbnRhaW5lci5zdHlsZS5oZWlnaHQ9dGhpcy5fdmlld3BvcnRFbGVtZW50LnN0eWxlLmhlaWdodCx0aGlzLl9zY3JlZW5FbGVtZW50LnN0eWxlLndpZHRoPXRoaXMuZGltZW5zaW9ucy5jYW52YXNXaWR0aCsicHgiLHRoaXMuX3NjcmVlbkVsZW1lbnQuc3R5bGUuaGVpZ2h0PXRoaXMuZGltZW5zaW9ucy5jYW52YXNIZWlnaHQrInB4In0sdC5wcm90b3R5cGUuc2V0Q29sb3JzPWZ1bmN0aW9uKGUpe3RoaXMuX2NvbG9ycz1lLHRoaXMuX2luamVjdENzcygpfSx0LnByb3RvdHlwZS5faW5qZWN0Q3NzPWZ1bmN0aW9uKCl7dmFyIGU9dGhpczt0aGlzLl90aGVtZVN0eWxlRWxlbWVudHx8KHRoaXMuX3RoZW1lU3R5bGVFbGVtZW50PWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInN0eWxlIiksdGhpcy5fc2NyZWVuRWxlbWVudC5hcHBlbmRDaGlsZCh0aGlzLl90aGVtZVN0eWxlRWxlbWVudCkpO3ZhciB0PXRoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cyB7IGNvbG9yOiAiK3RoaXMuX2NvbG9ycy5mb3JlZ3JvdW5kLmNzcysiOyBmb250LWZhbWlseTogIit0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZvbnRGYW1pbHkrIjsgZm9udC1zaXplOiAiK3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZm9udFNpemUrInB4O30iO3QrPXRoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiBzcGFuOm5vdCguIithLkJPTERfQ0xBU1MrIikgeyBmb250LXdlaWdodDogIit0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZvbnRXZWlnaHQrIjt9Iit0aGlzLl90ZXJtaW5hbFNlbGVjdG9yKyIgc3Bhbi4iK2EuQk9MRF9DTEFTUysiIHsgZm9udC13ZWlnaHQ6ICIrdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5mb250V2VpZ2h0Qm9sZCsiO30iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiBzcGFuLiIrYS5JVEFMSUNfQ0xBU1MrIiB7IGZvbnQtc3R5bGU6IGl0YWxpYzt9Iix0Kz0iQGtleWZyYW1lcyBibGlua19ib3hfc2hhZG93XyIrdGhpcy5fdGVybWluYWxDbGFzcysiIHsgNTAlIHsgIGJveC1zaGFkb3c6IG5vbmU7IH19Iix0Kz0iQGtleWZyYW1lcyBibGlua19ibG9ja18iK3RoaXMuX3Rlcm1pbmFsQ2xhc3MrIiB7IDAlIHsgIGJhY2tncm91bmQtY29sb3I6ICIrdGhpcy5fY29sb3JzLmN1cnNvci5jc3MrIjsgIGNvbG9yOiAiK3RoaXMuX2NvbG9ycy5jdXJzb3JBY2NlbnQuY3NzKyI7IH0gNTAlIHsgIGJhY2tncm91bmQtY29sb3I6ICIrdGhpcy5fY29sb3JzLmN1cnNvckFjY2VudC5jc3MrIjsgIGNvbG9yOiAiK3RoaXMuX2NvbG9ycy5jdXJzb3IuY3NzKyI7IH19Iix0Kz10aGlzLl90ZXJtaW5hbFNlbGVjdG9yKyIgLnh0ZXJtLXJvd3M6bm90KC54dGVybS1mb2N1cykgLiIrYS5DVVJTT1JfQ0xBU1MrIi4iK2EuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTKyIgeyBvdXRsaW5lOiAxcHggc29saWQgIit0aGlzLl9jb2xvcnMuY3Vyc29yLmNzcysiOyBvdXRsaW5lLW9mZnNldDogLTFweDt9Iit0aGlzLl90ZXJtaW5hbFNlbGVjdG9yKyIgLnh0ZXJtLXJvd3MueHRlcm0tZm9jdXMgLiIrYS5DVVJTT1JfQ0xBU1MrIi4iK2EuQ1VSU09SX0JMSU5LX0NMQVNTKyI6bm90KC4iK2EuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTKyIpIHsgYW5pbWF0aW9uOiBibGlua19ib3hfc2hhZG93XyIrdGhpcy5fdGVybWluYWxDbGFzcysiIDFzIHN0ZXAtZW5kIGluZmluaXRlO30iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cy54dGVybS1mb2N1cyAuIithLkNVUlNPUl9DTEFTUysiLiIrYS5DVVJTT1JfQkxJTktfQ0xBU1MrIi4iK2EuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTKyIgeyBhbmltYXRpb246IGJsaW5rX2Jsb2NrXyIrdGhpcy5fdGVybWluYWxDbGFzcysiIDFzIHN0ZXAtZW5kIGluZmluaXRlO30iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cy54dGVybS1mb2N1cyAuIithLkNVUlNPUl9DTEFTUysiLiIrYS5DVVJTT1JfU1RZTEVfQkxPQ0tfQ0xBU1MrIiB7IGJhY2tncm91bmQtY29sb3I6ICIrdGhpcy5fY29sb3JzLmN1cnNvci5jc3MrIjsgY29sb3I6ICIrdGhpcy5fY29sb3JzLmN1cnNvckFjY2VudC5jc3MrIjt9Iit0aGlzLl90ZXJtaW5hbFNlbGVjdG9yKyIgLnh0ZXJtLXJvd3MgLiIrYS5DVVJTT1JfQ0xBU1MrIi4iK2EuQ1VSU09SX1NUWUxFX0JBUl9DTEFTUysiIHsgYm94LXNoYWRvdzogIit0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmN1cnNvcldpZHRoKyJweCAwIDAgIit0aGlzLl9jb2xvcnMuY3Vyc29yLmNzcysiIGluc2V0O30iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cyAuIithLkNVUlNPUl9DTEFTUysiLiIrYS5DVVJTT1JfU1RZTEVfVU5ERVJMSU5FX0NMQVNTKyIgeyBib3gtc2hhZG93OiAwIC0xcHggMCAiK3RoaXMuX2NvbG9ycy5jdXJzb3IuY3NzKyIgaW5zZXQ7fSIsdCs9dGhpcy5fdGVybWluYWxTZWxlY3RvcisiIC54dGVybS1zZWxlY3Rpb24geyBwb3NpdGlvbjogYWJzb2x1dGU7IHRvcDogMDsgbGVmdDogMDsgei1pbmRleDogMTsgcG9pbnRlci1ldmVudHM6IG5vbmU7fSIrdGhpcy5fdGVybWluYWxTZWxlY3RvcisiIC54dGVybS1zZWxlY3Rpb24gZGl2IHsgcG9zaXRpb246IGFic29sdXRlOyBiYWNrZ3JvdW5kLWNvbG9yOiAiK3RoaXMuX2NvbG9ycy5zZWxlY3Rpb25UcmFuc3BhcmVudC5jc3MrIjt9Iix0aGlzLl9jb2xvcnMuYW5zaS5mb3JFYWNoKChmdW5jdGlvbihyLGkpe3QrPWUuX3Rlcm1pbmFsU2VsZWN0b3IrIiAuIit2K2krIiB7IGNvbG9yOiAiK3IuY3NzKyI7IH0iK2UuX3Rlcm1pbmFsU2VsZWN0b3IrIiAuIitnK2krIiB7IGJhY2tncm91bmQtY29sb3I6ICIrci5jc3MrIjsgfSJ9KSksdCs9dGhpcy5fdGVybWluYWxTZWxlY3RvcisiIC4iK3YrYy5JTlZFUlRFRF9ERUZBVUxUX0NPTE9SKyIgeyBjb2xvcjogIitfLmNvbG9yLm9wYXF1ZSh0aGlzLl9jb2xvcnMuYmFja2dyb3VuZCkuY3NzKyI7IH0iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAuIitnK2MuSU5WRVJURURfREVGQVVMVF9DT0xPUisiIHsgYmFja2dyb3VuZC1jb2xvcjogIit0aGlzLl9jb2xvcnMuZm9yZWdyb3VuZC5jc3MrIjsgfSIsdGhpcy5fdGhlbWVTdHlsZUVsZW1lbnQudGV4dENvbnRlbnQ9dH0sdC5wcm90b3R5cGUub25EZXZpY2VQaXhlbFJhdGlvQ2hhbmdlPWZ1bmN0aW9uKCl7dGhpcy5fdXBkYXRlRGltZW5zaW9ucygpfSx0LnByb3RvdHlwZS5fcmVmcmVzaFJvd0VsZW1lbnRzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRoaXMuX3Jvd0VsZW1lbnRzLmxlbmd0aDtyPD10O3IrKyl7dmFyIGk9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iik7dGhpcy5fcm93Q29udGFpbmVyLmFwcGVuZENoaWxkKGkpLHRoaXMuX3Jvd0VsZW1lbnRzLnB1c2goaSl9Zm9yKDt0aGlzLl9yb3dFbGVtZW50cy5sZW5ndGg+dDspdGhpcy5fcm93Q29udGFpbmVyLnJlbW92ZUNoaWxkKHRoaXMuX3Jvd0VsZW1lbnRzLnBvcCgpKX0sdC5wcm90b3R5cGUub25SZXNpemU9ZnVuY3Rpb24oZSx0KXt0aGlzLl9yZWZyZXNoUm93RWxlbWVudHMoZSx0KSx0aGlzLl91cGRhdGVEaW1lbnNpb25zKCl9LHQucHJvdG90eXBlLm9uQ2hhclNpemVDaGFuZ2VkPWZ1bmN0aW9uKCl7dGhpcy5fdXBkYXRlRGltZW5zaW9ucygpfSx0LnByb3RvdHlwZS5vbkJsdXI9ZnVuY3Rpb24oKXt0aGlzLl9yb3dDb250YWluZXIuY2xhc3NMaXN0LnJlbW92ZSh5KX0sdC5wcm90b3R5cGUub25Gb2N1cz1mdW5jdGlvbigpe3RoaXMuX3Jvd0NvbnRhaW5lci5jbGFzc0xpc3QuYWRkKHkpfSx0LnByb3RvdHlwZS5vblNlbGVjdGlvbkNoYW5nZWQ9ZnVuY3Rpb24oZSx0LHIpe2Zvcig7dGhpcy5fc2VsZWN0aW9uQ29udGFpbmVyLmNoaWxkcmVuLmxlbmd0aDspdGhpcy5fc2VsZWN0aW9uQ29udGFpbmVyLnJlbW92ZUNoaWxkKHRoaXMuX3NlbGVjdGlvbkNvbnRhaW5lci5jaGlsZHJlblswXSk7aWYoZSYmdCl7dmFyIGk9ZVsxXS10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCxuPXRbMV0tdGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3Asbz1NYXRoLm1heChpLDApLHM9TWF0aC5taW4obix0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSk7aWYoIShvPj10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3N8fHM8MCkpe3ZhciBhPWRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtpZihyKWEuYXBwZW5kQ2hpbGQodGhpcy5fY3JlYXRlU2VsZWN0aW9uRWxlbWVudChvLGVbMF0sdFswXSxzLW8rMSkpO2Vsc2V7dmFyIGM9aT09PW8/ZVswXTowLGw9bz09PW4/dFswXTp0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM7YS5hcHBlbmRDaGlsZCh0aGlzLl9jcmVhdGVTZWxlY3Rpb25FbGVtZW50KG8sYyxsKSk7dmFyIHU9cy1vLTE7aWYoYS5hcHBlbmRDaGlsZCh0aGlzLl9jcmVhdGVTZWxlY3Rpb25FbGVtZW50KG8rMSwwLHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx1KSksbyE9PXMpe3ZhciBoPW49PT1zP3RbMF06dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzO2EuYXBwZW5kQ2hpbGQodGhpcy5fY3JlYXRlU2VsZWN0aW9uRWxlbWVudChzLDAsaCkpfX10aGlzLl9zZWxlY3Rpb25Db250YWluZXIuYXBwZW5kQ2hpbGQoYSl9fX0sdC5wcm90b3R5cGUuX2NyZWF0ZVNlbGVjdGlvbkVsZW1lbnQ9ZnVuY3Rpb24oZSx0LHIsaSl7dm9pZCAwPT09aSYmKGk9MSk7dmFyIG49ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iik7cmV0dXJuIG4uc3R5bGUuaGVpZ2h0PWkqdGhpcy5kaW1lbnNpb25zLmFjdHVhbENlbGxIZWlnaHQrInB4IixuLnN0eWxlLnRvcD1lKnRoaXMuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KyJweCIsbi5zdHlsZS5sZWZ0PXQqdGhpcy5kaW1lbnNpb25zLmFjdHVhbENlbGxXaWR0aCsicHgiLG4uc3R5bGUud2lkdGg9dGhpcy5kaW1lbnNpb25zLmFjdHVhbENlbGxXaWR0aCooci10KSsicHgiLG59LHQucHJvdG90eXBlLm9uQ3Vyc29yTW92ZT1mdW5jdGlvbigpe30sdC5wcm90b3R5cGUub25PcHRpb25zQ2hhbmdlZD1mdW5jdGlvbigpe3RoaXMuX3VwZGF0ZURpbWVuc2lvbnMoKSx0aGlzLl9pbmplY3RDc3MoKX0sdC5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXtmb3IodmFyIGU9MCx0PXRoaXMuX3Jvd0VsZW1lbnRzO2U8dC5sZW5ndGg7ZSsrKXRbZV0uaW5uZXJUZXh0PSIifSx0LnByb3RvdHlwZS5yZW5kZXJSb3dzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnliYXNlK3RoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnksaT1NYXRoLm1pbih0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci54LHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scy0xKSxuPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yQmxpbmssbz1lO288PXQ7bysrKXt2YXIgcz10aGlzLl9yb3dFbGVtZW50c1tvXTtzLmlubmVyVGV4dD0iIjt2YXIgYT1vK3RoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwLGM9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KGEpLGw9dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JTdHlsZTtzLmFwcGVuZENoaWxkKHRoaXMuX3Jvd0ZhY3RvcnkuY3JlYXRlUm93KGMsYSxhPT09cixsLGksbix0aGlzLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoLHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scykpfX0sT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJfdGVybWluYWxTZWxlY3RvciIse2dldDpmdW5jdGlvbigpe3JldHVybiIuIitwK3RoaXMuX3Rlcm1pbmFsQ2xhc3N9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuX29uTGlua0hvdmVyPWZ1bmN0aW9uKGUpe3RoaXMuX3NldENlbGxVbmRlcmxpbmUoZS54MSxlLngyLGUueTEsZS55MixlLmNvbHMsITApfSx0LnByb3RvdHlwZS5fb25MaW5rTGVhdmU9ZnVuY3Rpb24oZSl7dGhpcy5fc2V0Q2VsbFVuZGVybGluZShlLngxLGUueDIsZS55MSxlLnkyLGUuY29scywhMSl9LHQucHJvdG90eXBlLl9zZXRDZWxsVW5kZXJsaW5lPWZ1bmN0aW9uKGUsdCxyLGksbixvKXtmb3IoO2UhPT10fHxyIT09aTspe3ZhciBzPXRoaXMuX3Jvd0VsZW1lbnRzW3JdO2lmKCFzKXJldHVybjt2YXIgYT1zLmNoaWxkcmVuW2VdO2EmJihhLnN0eWxlLnRleHREZWNvcmF0aW9uPW8/InVuZGVybGluZSI6Im5vbmUiKSwrK2U+PW4mJihlPTAscisrKX19LG8oW3MoNixoLklJbnN0YW50aWF0aW9uU2VydmljZSkscyg3LHUuSUNoYXJTaXplU2VydmljZSkscyg4LGguSU9wdGlvbnNTZXJ2aWNlKSxzKDksaC5JQnVmZmVyU2VydmljZSldLHQpfShsLkRpc3Bvc2FibGUpO3QuRG9tUmVuZGVyZXI9Yn0sMzc4NzpmdW5jdGlvbihlLHQscil7dmFyIGk9dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxuPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkRvbVJlbmRlcmVyUm93RmFjdG9yeT10LkNVUlNPUl9TVFlMRV9VTkRFUkxJTkVfQ0xBU1M9dC5DVVJTT1JfU1RZTEVfQkFSX0NMQVNTPXQuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTPXQuQ1VSU09SX0JMSU5LX0NMQVNTPXQuQ1VSU09SX0NMQVNTPXQuU1RSSUtFVEhST1VHSF9DTEFTUz10LlVOREVSTElORV9DTEFTUz10LklUQUxJQ19DTEFTUz10LkRJTV9DTEFTUz10LkJPTERfQ0xBU1M9dm9pZCAwO3ZhciBvPXIoODgwMykscz1yKDY0MyksYT1yKDUxMSksYz1yKDI1ODUpLGw9cig0Nzc0KSx1PXIoNDcyNSksaD1yKDQyNjkpO3QuQk9MRF9DTEFTUz0ieHRlcm0tYm9sZCIsdC5ESU1fQ0xBU1M9Inh0ZXJtLWRpbSIsdC5JVEFMSUNfQ0xBU1M9Inh0ZXJtLWl0YWxpYyIsdC5VTkRFUkxJTkVfQ0xBU1M9Inh0ZXJtLXVuZGVybGluZSIsdC5TVFJJS0VUSFJPVUdIX0NMQVNTPSJ4dGVybS1zdHJpa2V0aHJvdWdoIix0LkNVUlNPUl9DTEFTUz0ieHRlcm0tY3Vyc29yIix0LkNVUlNPUl9CTElOS19DTEFTUz0ieHRlcm0tY3Vyc29yLWJsaW5rIix0LkNVUlNPUl9TVFlMRV9CTE9DS19DTEFTUz0ieHRlcm0tY3Vyc29yLWJsb2NrIix0LkNVUlNPUl9TVFlMRV9CQVJfQ0xBU1M9Inh0ZXJtLWN1cnNvci1iYXIiLHQuQ1VSU09SX1NUWUxFX1VOREVSTElORV9DTEFTUz0ieHRlcm0tY3Vyc29yLXVuZGVybGluZSI7dmFyIGY9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyLGksbil7dGhpcy5fZG9jdW1lbnQ9ZSx0aGlzLl9jb2xvcnM9dCx0aGlzLl9jaGFyYWN0ZXJKb2luZXJTZXJ2aWNlPXIsdGhpcy5fb3B0aW9uc1NlcnZpY2U9aSx0aGlzLl9jb3JlU2VydmljZT1uLHRoaXMuX3dvcmtDZWxsPW5ldyBhLkNlbGxEYXRhfXJldHVybiBlLnByb3RvdHlwZS5zZXRDb2xvcnM9ZnVuY3Rpb24oZSl7dGhpcy5fY29sb3JzPWV9LGUucHJvdG90eXBlLmNyZWF0ZVJvdz1mdW5jdGlvbihlLHIsaSxuLGEsYyx1LGYpe2Zvcih2YXIgZD10aGlzLl9kb2N1bWVudC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCkscD10aGlzLl9jaGFyYWN0ZXJKb2luZXJTZXJ2aWNlLmdldEpvaW5lZENoYXJhY3RlcnMociksdj0wLGc9TWF0aC5taW4oZS5sZW5ndGgsZiktMTtnPj0wO2ctLSlpZihlLmxvYWRDZWxsKGcsdGhpcy5fd29ya0NlbGwpLmdldENvZGUoKSE9PXMuTlVMTF9DRUxMX0NPREV8fGkmJmc9PT1hKXt2PWcrMTticmVha31mb3IoZz0wO2c8djtnKyspe2UubG9hZENlbGwoZyx0aGlzLl93b3JrQ2VsbCk7dmFyIHk9dGhpcy5fd29ya0NlbGwuZ2V0V2lkdGgoKTtpZigwIT09eSl7dmFyIG09ITEsYj1nLFM9dGhpcy5fd29ya0NlbGw7aWYocC5sZW5ndGg+MCYmZz09PXBbMF1bMF0pe209ITA7dmFyIEM9cC5zaGlmdCgpO1M9bmV3IGguSm9pbmVkQ2VsbERhdGEodGhpcy5fd29ya0NlbGwsZS50cmFuc2xhdGVUb1N0cmluZyghMCxDWzBdLENbMV0pLENbMV0tQ1swXSksYj1DWzFdLTEseT1TLmdldFdpZHRoKCl9dmFyIHc9dGhpcy5fZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3BhbiIpO2lmKHk+MSYmKHcuc3R5bGUud2lkdGg9dSp5KyJweCIpLG0mJih3LnN0eWxlLmRpc3BsYXk9ImlubGluZSIsYT49ZyYmYTw9YiYmKGE9ZykpLCF0aGlzLl9jb3JlU2VydmljZS5pc0N1cnNvckhpZGRlbiYmaSYmZz09PWEpc3dpdGNoKHcuY2xhc3NMaXN0LmFkZCh0LkNVUlNPUl9DTEFTUyksYyYmdy5jbGFzc0xpc3QuYWRkKHQuQ1VSU09SX0JMSU5LX0NMQVNTKSxuKXtjYXNlImJhciI6dy5jbGFzc0xpc3QuYWRkKHQuQ1VSU09SX1NUWUxFX0JBUl9DTEFTUyk7YnJlYWs7Y2FzZSJ1bmRlcmxpbmUiOncuY2xhc3NMaXN0LmFkZCh0LkNVUlNPUl9TVFlMRV9VTkRFUkxJTkVfQ0xBU1MpO2JyZWFrO2RlZmF1bHQ6dy5jbGFzc0xpc3QuYWRkKHQuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTKX1TLmlzQm9sZCgpJiZ3LmNsYXNzTGlzdC5hZGQodC5CT0xEX0NMQVNTKSxTLmlzSXRhbGljKCkmJncuY2xhc3NMaXN0LmFkZCh0LklUQUxJQ19DTEFTUyksUy5pc0RpbSgpJiZ3LmNsYXNzTGlzdC5hZGQodC5ESU1fQ0xBU1MpLFMuaXNVbmRlcmxpbmUoKSYmdy5jbGFzc0xpc3QuYWRkKHQuVU5ERVJMSU5FX0NMQVNTKSxTLmlzSW52aXNpYmxlKCk/dy50ZXh0Q29udGVudD1zLldISVRFU1BBQ0VfQ0VMTF9DSEFSOncudGV4dENvbnRlbnQ9Uy5nZXRDaGFycygpfHxzLldISVRFU1BBQ0VfQ0VMTF9DSEFSLFMuaXNTdHJpa2V0aHJvdWdoKCkmJncuY2xhc3NMaXN0LmFkZCh0LlNUUklLRVRIUk9VR0hfQ0xBU1MpO3ZhciBMPVMuZ2V0RmdDb2xvcigpLEU9Uy5nZXRGZ0NvbG9yTW9kZSgpLHg9Uy5nZXRCZ0NvbG9yKCksQT1TLmdldEJnQ29sb3JNb2RlKCksaz0hIVMuaXNJbnZlcnNlKCk7aWYoayl7dmFyIE09TDtMPXgseD1NO3ZhciBSPUU7RT1BLEE9Un1zd2l0Y2goRSl7Y2FzZSAxNjc3NzIxNjpjYXNlIDMzNTU0NDMyOlMuaXNCb2xkKCkmJkw8OCYmdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5kcmF3Qm9sZFRleHRJbkJyaWdodENvbG9ycyYmKEwrPTgpLHRoaXMuX2FwcGx5TWluaW11bUNvbnRyYXN0KHcsdGhpcy5fY29sb3JzLmJhY2tncm91bmQsdGhpcy5fY29sb3JzLmFuc2lbTF0pfHx3LmNsYXNzTGlzdC5hZGQoInh0ZXJtLWZnLSIrTCk7YnJlYWs7Y2FzZSA1MDMzMTY0ODp2YXIgVD1sLnJnYmEudG9Db2xvcihMPj4xNiYyNTUsTD4+OCYyNTUsMjU1JkwpO3RoaXMuX2FwcGx5TWluaW11bUNvbnRyYXN0KHcsdGhpcy5fY29sb3JzLmJhY2tncm91bmQsVCl8fHRoaXMuX2FkZFN0eWxlKHcsImNvbG9yOiMiK18oTC50b1N0cmluZygxNiksIjAiLDYpKTticmVhaztkZWZhdWx0OnRoaXMuX2FwcGx5TWluaW11bUNvbnRyYXN0KHcsdGhpcy5fY29sb3JzLmJhY2tncm91bmQsdGhpcy5fY29sb3JzLmZvcmVncm91bmQpfHxrJiZ3LmNsYXNzTGlzdC5hZGQoInh0ZXJtLWZnLSIrby5JTlZFUlRFRF9ERUZBVUxUX0NPTE9SKX1zd2l0Y2goQSl7Y2FzZSAxNjc3NzIxNjpjYXNlIDMzNTU0NDMyOncuY2xhc3NMaXN0LmFkZCgieHRlcm0tYmctIit4KTticmVhaztjYXNlIDUwMzMxNjQ4OnRoaXMuX2FkZFN0eWxlKHcsImJhY2tncm91bmQtY29sb3I6IyIrXyh4LnRvU3RyaW5nKDE2KSwiMCIsNikpO2JyZWFrO2RlZmF1bHQ6ayYmdy5jbGFzc0xpc3QuYWRkKCJ4dGVybS1iZy0iK28uSU5WRVJURURfREVGQVVMVF9DT0xPUil9ZC5hcHBlbmRDaGlsZCh3KSxnPWJ9fXJldHVybiBkfSxlLnByb3RvdHlwZS5fYXBwbHlNaW5pbXVtQ29udHJhc3Q9ZnVuY3Rpb24oZSx0LHIpe2lmKDE9PT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLm1pbmltdW1Db250cmFzdFJhdGlvKXJldHVybiExO3ZhciBpPXRoaXMuX2NvbG9ycy5jb250cmFzdENhY2hlLmdldENvbG9yKHRoaXMuX3dvcmtDZWxsLmJnLHRoaXMuX3dvcmtDZWxsLmZnKTtyZXR1cm4gdm9pZCAwPT09aSYmKGk9bC5jb2xvci5lbnN1cmVDb250cmFzdFJhdGlvKHQscix0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLm1pbmltdW1Db250cmFzdFJhdGlvKSx0aGlzLl9jb2xvcnMuY29udHJhc3RDYWNoZS5zZXRDb2xvcih0aGlzLl93b3JrQ2VsbC5iZyx0aGlzLl93b3JrQ2VsbC5mZyxudWxsIT1pP2k6bnVsbCkpLCEhaSYmKHRoaXMuX2FkZFN0eWxlKGUsImNvbG9yOiIraS5jc3MpLCEwKX0sZS5wcm90b3R5cGUuX2FkZFN0eWxlPWZ1bmN0aW9uKGUsdCl7ZS5zZXRBdHRyaWJ1dGUoInN0eWxlIiwiIisoZS5nZXRBdHRyaWJ1dGUoInN0eWxlIil8fCIiKSt0KyI7Iil9LGkoW24oMix1LklDaGFyYWN0ZXJKb2luZXJTZXJ2aWNlKSxuKDMsYy5JT3B0aW9uc1NlcnZpY2UpLG4oNCxjLklDb3JlU2VydmljZSldLGUpfSgpO2Z1bmN0aW9uIF8oZSx0LHIpe2Zvcig7ZS5sZW5ndGg8cjspZT10K2U7cmV0dXJuIGV9dC5Eb21SZW5kZXJlclJvd0ZhY3Rvcnk9Zn0sNDU2OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuU2VsZWN0aW9uTW9kZWw9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9idWZmZXJTZXJ2aWNlPWUsdGhpcy5pc1NlbGVjdEFsbEFjdGl2ZT0hMSx0aGlzLnNlbGVjdGlvblN0YXJ0TGVuZ3RoPTB9cmV0dXJuIGUucHJvdG90eXBlLmNsZWFyU2VsZWN0aW9uPWZ1bmN0aW9uKCl7dGhpcy5zZWxlY3Rpb25TdGFydD12b2lkIDAsdGhpcy5zZWxlY3Rpb25FbmQ9dm9pZCAwLHRoaXMuaXNTZWxlY3RBbGxBY3RpdmU9ITEsdGhpcy5zZWxlY3Rpb25TdGFydExlbmd0aD0wfSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImZpbmFsU2VsZWN0aW9uU3RhcnQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pc1NlbGVjdEFsbEFjdGl2ZT9bMCwwXTp0aGlzLnNlbGVjdGlvbkVuZCYmdGhpcy5zZWxlY3Rpb25TdGFydCYmdGhpcy5hcmVTZWxlY3Rpb25WYWx1ZXNSZXZlcnNlZCgpP3RoaXMuc2VsZWN0aW9uRW5kOnRoaXMuc2VsZWN0aW9uU3RhcnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJmaW5hbFNlbGVjdGlvbkVuZCIse2dldDpmdW5jdGlvbigpe2lmKHRoaXMuaXNTZWxlY3RBbGxBY3RpdmUpcmV0dXJuW3RoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55YmFzZSt0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMV07aWYodGhpcy5zZWxlY3Rpb25TdGFydCl7aWYoIXRoaXMuc2VsZWN0aW9uRW5kfHx0aGlzLmFyZVNlbGVjdGlvblZhbHVlc1JldmVyc2VkKCkpe3ZhciBlPXRoaXMuc2VsZWN0aW9uU3RhcnRbMF0rdGhpcy5zZWxlY3Rpb25TdGFydExlbmd0aDtyZXR1cm4gZT50aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM/ZSV0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM9PTA/W3RoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLnNlbGVjdGlvblN0YXJ0WzFdK01hdGguZmxvb3IoZS90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpLTFdOltlJXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLnNlbGVjdGlvblN0YXJ0WzFdK01hdGguZmxvb3IoZS90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpXTpbZSx0aGlzLnNlbGVjdGlvblN0YXJ0WzFdXX1yZXR1cm4gdGhpcy5zZWxlY3Rpb25TdGFydExlbmd0aCYmdGhpcy5zZWxlY3Rpb25FbmRbMV09PT10aGlzLnNlbGVjdGlvblN0YXJ0WzFdP1tNYXRoLm1heCh0aGlzLnNlbGVjdGlvblN0YXJ0WzBdK3RoaXMuc2VsZWN0aW9uU3RhcnRMZW5ndGgsdGhpcy5zZWxlY3Rpb25FbmRbMF0pLHRoaXMuc2VsZWN0aW9uRW5kWzFdXTp0aGlzLnNlbGVjdGlvbkVuZH19LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUuYXJlU2VsZWN0aW9uVmFsdWVzUmV2ZXJzZWQ9ZnVuY3Rpb24oKXt2YXIgZT10aGlzLnNlbGVjdGlvblN0YXJ0LHQ9dGhpcy5zZWxlY3Rpb25FbmQ7cmV0dXJuISghZXx8IXQpJiYoZVsxXT50WzFdfHxlWzFdPT09dFsxXSYmZVswXT50WzBdKX0sZS5wcm90b3R5cGUub25UcmltPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLnNlbGVjdGlvblN0YXJ0JiYodGhpcy5zZWxlY3Rpb25TdGFydFsxXS09ZSksdGhpcy5zZWxlY3Rpb25FbmQmJih0aGlzLnNlbGVjdGlvbkVuZFsxXS09ZSksdGhpcy5zZWxlY3Rpb25FbmQmJnRoaXMuc2VsZWN0aW9uRW5kWzFdPDA/KHRoaXMuY2xlYXJTZWxlY3Rpb24oKSwhMCk6KHRoaXMuc2VsZWN0aW9uU3RhcnQmJnRoaXMuc2VsZWN0aW9uU3RhcnRbMV08MCYmKHRoaXMuc2VsZWN0aW9uU3RhcnRbMV09MCksITEpfSxlfSgpO3QuU2VsZWN0aW9uTW9kZWw9cn0sNDI4OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaT10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LG49dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQ2hhclNpemVTZXJ2aWNlPXZvaWQgMDt2YXIgbz1yKDI1ODUpLHM9cig4NDYwKSxhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQscil7dGhpcy5fb3B0aW9uc1NlcnZpY2U9cix0aGlzLndpZHRoPTAsdGhpcy5oZWlnaHQ9MCx0aGlzLl9vbkNoYXJTaXplQ2hhbmdlPW5ldyBzLkV2ZW50RW1pdHRlcix0aGlzLl9tZWFzdXJlU3RyYXRlZ3k9bmV3IGMoZSx0LHRoaXMuX29wdGlvbnNTZXJ2aWNlKX1yZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJoYXNWYWxpZFNpemUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy53aWR0aD4wJiZ0aGlzLmhlaWdodD4wfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25DaGFyU2l6ZUNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkNoYXJTaXplQ2hhbmdlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLm1lYXN1cmU9ZnVuY3Rpb24oKXt2YXIgZT10aGlzLl9tZWFzdXJlU3RyYXRlZ3kubWVhc3VyZSgpO2Uud2lkdGg9PT10aGlzLndpZHRoJiZlLmhlaWdodD09PXRoaXMuaGVpZ2h0fHwodGhpcy53aWR0aD1lLndpZHRoLHRoaXMuaGVpZ2h0PWUuaGVpZ2h0LHRoaXMuX29uQ2hhclNpemVDaGFuZ2UuZmlyZSgpKX0saShbbigyLG8uSU9wdGlvbnNTZXJ2aWNlKV0sZSl9KCk7dC5DaGFyU2l6ZVNlcnZpY2U9YTt2YXIgYz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSx0LHIpe3RoaXMuX2RvY3VtZW50PWUsdGhpcy5fcGFyZW50RWxlbWVudD10LHRoaXMuX29wdGlvbnNTZXJ2aWNlPXIsdGhpcy5fcmVzdWx0PXt3aWR0aDowLGhlaWdodDowfSx0aGlzLl9tZWFzdXJlRWxlbWVudD10aGlzLl9kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzcGFuIiksdGhpcy5fbWVhc3VyZUVsZW1lbnQuY2xhc3NMaXN0LmFkZCgieHRlcm0tY2hhci1tZWFzdXJlLWVsZW1lbnQiKSx0aGlzLl9tZWFzdXJlRWxlbWVudC50ZXh0Q29udGVudD0iVyIsdGhpcy5fbWVhc3VyZUVsZW1lbnQuc2V0QXR0cmlidXRlKCJhcmlhLWhpZGRlbiIsInRydWUiKSx0aGlzLl9wYXJlbnRFbGVtZW50LmFwcGVuZENoaWxkKHRoaXMuX21lYXN1cmVFbGVtZW50KX1yZXR1cm4gZS5wcm90b3R5cGUubWVhc3VyZT1mdW5jdGlvbigpe3RoaXMuX21lYXN1cmVFbGVtZW50LnN0eWxlLmZvbnRGYW1pbHk9dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5mb250RmFtaWx5LHRoaXMuX21lYXN1cmVFbGVtZW50LnN0eWxlLmZvbnRTaXplPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZm9udFNpemUrInB4Ijt2YXIgZT10aGlzLl9tZWFzdXJlRWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtyZXR1cm4gMCE9PWUud2lkdGgmJjAhPT1lLmhlaWdodCYmKHRoaXMuX3Jlc3VsdC53aWR0aD1lLndpZHRoLHRoaXMuX3Jlc3VsdC5oZWlnaHQ9TWF0aC5jZWlsKGUuaGVpZ2h0KSksdGhpcy5fcmVzdWx0fSxlfSgpfSw0MjY5OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkNoYXJhY3RlckpvaW5lclNlcnZpY2U9dC5Kb2luZWRDZWxsRGF0YT12b2lkIDA7dmFyIGE9cigzNzM0KSxjPXIoNjQzKSxsPXIoNTExKSx1PXIoMjU4NSksaD1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpKXt2YXIgbj1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIG4uY29udGVudD0wLG4uY29tYmluZWREYXRhPSIiLG4uZmc9dC5mZyxuLmJnPXQuYmcsbi5jb21iaW5lZERhdGE9cixuLl93aWR0aD1pLG59cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5pc0NvbWJpbmVkPWZ1bmN0aW9uKCl7cmV0dXJuIDIwOTcxNTJ9LHQucHJvdG90eXBlLmdldFdpZHRoPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX3dpZHRofSx0LnByb3RvdHlwZS5nZXRDaGFycz1mdW5jdGlvbigpe3JldHVybiB0aGlzLmNvbWJpbmVkRGF0YX0sdC5wcm90b3R5cGUuZ2V0Q29kZT1mdW5jdGlvbigpe3JldHVybiAyMDk3MTUxfSx0LnByb3RvdHlwZS5zZXRGcm9tQ2hhckRhdGE9ZnVuY3Rpb24oZSl7dGhyb3cgbmV3IEVycm9yKCJub3QgaW1wbGVtZW50ZWQiKX0sdC5wcm90b3R5cGUuZ2V0QXNDaGFyRGF0YT1mdW5jdGlvbigpe3JldHVyblt0aGlzLmZnLHRoaXMuZ2V0Q2hhcnMoKSx0aGlzLmdldFdpZHRoKCksdGhpcy5nZXRDb2RlKCldfSx0fShhLkF0dHJpYnV0ZURhdGEpO3QuSm9pbmVkQ2VsbERhdGE9aDt2YXIgZj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fYnVmZmVyU2VydmljZT1lLHRoaXMuX2NoYXJhY3RlckpvaW5lcnM9W10sdGhpcy5fbmV4dENoYXJhY3RlckpvaW5lcklkPTAsdGhpcy5fd29ya0NlbGw9bmV3IGwuQ2VsbERhdGF9cmV0dXJuIGUucHJvdG90eXBlLnJlZ2lzdGVyPWZ1bmN0aW9uKGUpe3ZhciB0PXtpZDp0aGlzLl9uZXh0Q2hhcmFjdGVySm9pbmVySWQrKyxoYW5kbGVyOmV9O3JldHVybiB0aGlzLl9jaGFyYWN0ZXJKb2luZXJzLnB1c2godCksdC5pZH0sZS5wcm90b3R5cGUuZGVyZWdpc3Rlcj1mdW5jdGlvbihlKXtmb3IodmFyIHQ9MDt0PHRoaXMuX2NoYXJhY3RlckpvaW5lcnMubGVuZ3RoO3QrKylpZih0aGlzLl9jaGFyYWN0ZXJKb2luZXJzW3RdLmlkPT09ZSlyZXR1cm4gdGhpcy5fY2hhcmFjdGVySm9pbmVycy5zcGxpY2UodCwxKSwhMDtyZXR1cm4hMX0sZS5wcm90b3R5cGUuZ2V0Sm9pbmVkQ2hhcmFjdGVycz1mdW5jdGlvbihlKXtpZigwPT09dGhpcy5fY2hhcmFjdGVySm9pbmVycy5sZW5ndGgpcmV0dXJuW107dmFyIHQ9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KGUpO2lmKCF0fHwwPT09dC5sZW5ndGgpcmV0dXJuW107Zm9yKHZhciByPVtdLGk9dC50cmFuc2xhdGVUb1N0cmluZyghMCksbj0wLG89MCxzPTAsYT10LmdldEZnKDApLGw9dC5nZXRCZygwKSx1PTA7dTx0LmdldFRyaW1tZWRMZW5ndGgoKTt1KyspaWYodC5sb2FkQ2VsbCh1LHRoaXMuX3dvcmtDZWxsKSwwIT09dGhpcy5fd29ya0NlbGwuZ2V0V2lkdGgoKSl7aWYodGhpcy5fd29ya0NlbGwuZmchPT1hfHx0aGlzLl93b3JrQ2VsbC5iZyE9PWwpe2lmKHUtbj4xKWZvcih2YXIgaD10aGlzLl9nZXRKb2luZWRSYW5nZXMoaSxzLG8sdCxuKSxmPTA7ZjxoLmxlbmd0aDtmKyspci5wdXNoKGhbZl0pO249dSxzPW8sYT10aGlzLl93b3JrQ2VsbC5mZyxsPXRoaXMuX3dvcmtDZWxsLmJnfW8rPXRoaXMuX3dvcmtDZWxsLmdldENoYXJzKCkubGVuZ3RofHxjLldISVRFU1BBQ0VfQ0VMTF9DSEFSLmxlbmd0aH1pZih0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtbj4xKWZvcihoPXRoaXMuX2dldEpvaW5lZFJhbmdlcyhpLHMsbyx0LG4pLGY9MDtmPGgubGVuZ3RoO2YrKylyLnB1c2goaFtmXSk7cmV0dXJuIHJ9LGUucHJvdG90eXBlLl9nZXRKb2luZWRSYW5nZXM9ZnVuY3Rpb24odCxyLGksbixvKXt2YXIgcz10LnN1YnN0cmluZyhyLGkpLGE9W107dHJ5e2E9dGhpcy5fY2hhcmFjdGVySm9pbmVyc1swXS5oYW5kbGVyKHMpfWNhdGNoKGUpe2NvbnNvbGUuZXJyb3IoZSl9Zm9yKHZhciBjPTE7Yzx0aGlzLl9jaGFyYWN0ZXJKb2luZXJzLmxlbmd0aDtjKyspdHJ5e2Zvcih2YXIgbD10aGlzLl9jaGFyYWN0ZXJKb2luZXJzW2NdLmhhbmRsZXIocyksdT0wO3U8bC5sZW5ndGg7dSsrKWUuX21lcmdlUmFuZ2VzKGEsbFt1XSl9Y2F0Y2goZSl7Y29uc29sZS5lcnJvcihlKX1yZXR1cm4gdGhpcy5fc3RyaW5nUmFuZ2VzVG9DZWxsUmFuZ2VzKGEsbixvKSxhfSxlLnByb3RvdHlwZS5fc3RyaW5nUmFuZ2VzVG9DZWxsUmFuZ2VzPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT0wLG49ITEsbz0wLHM9ZVtpXTtpZihzKXtmb3IodmFyIGE9cjthPHRoaXMuX2J1ZmZlclNlcnZpY2UuY29sczthKyspe3ZhciBsPXQuZ2V0V2lkdGgoYSksdT10LmdldFN0cmluZyhhKS5sZW5ndGh8fGMuV0hJVEVTUEFDRV9DRUxMX0NIQVIubGVuZ3RoO2lmKDAhPT1sKXtpZighbiYmc1swXTw9byYmKHNbMF09YSxuPSEwKSxzWzFdPD1vKXtpZihzWzFdPWEsIShzPWVbKytpXSkpYnJlYWs7c1swXTw9bz8oc1swXT1hLG49ITApOm49ITF9bys9dX19cyYmKHNbMV09dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKX19LGUuX21lcmdlUmFuZ2VzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPSExLGk9MDtpPGUubGVuZ3RoO2krKyl7dmFyIG49ZVtpXTtpZihyKXtpZih0WzFdPD1uWzBdKXJldHVybiBlW2ktMV1bMV09dFsxXSxlO2lmKHRbMV08PW5bMV0pcmV0dXJuIGVbaS0xXVsxXT1NYXRoLm1heCh0WzFdLG5bMV0pLGUuc3BsaWNlKGksMSksZTtlLnNwbGljZShpLDEpLGktLX1lbHNle2lmKHRbMV08PW5bMF0pcmV0dXJuIGUuc3BsaWNlKGksMCx0KSxlO2lmKHRbMV08PW5bMV0pcmV0dXJuIG5bMF09TWF0aC5taW4odFswXSxuWzBdKSxlO3RbMF08blsxXSYmKG5bMF09TWF0aC5taW4odFswXSxuWzBdKSxyPSEwKX19cmV0dXJuIHI/ZVtlLmxlbmd0aC0xXVsxXT10WzFdOmUucHVzaCh0KSxlfSxlPW8oW3MoMCx1LklCdWZmZXJTZXJ2aWNlKV0sZSl9KCk7dC5DaGFyYWN0ZXJKb2luZXJTZXJ2aWNlPWZ9LDUxMTQ6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Db3JlQnJvd3NlclNlcnZpY2U9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl90ZXh0YXJlYT1lfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImlzRm9jdXNlZCIse2dldDpmdW5jdGlvbigpe3JldHVybih0aGlzLl90ZXh0YXJlYS5nZXRSb290Tm9kZT90aGlzLl90ZXh0YXJlYS5nZXRSb290Tm9kZSgpOmRvY3VtZW50KS5hY3RpdmVFbGVtZW50PT09dGhpcy5fdGV4dGFyZWEmJmRvY3VtZW50Lmhhc0ZvY3VzKCl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZX0oKTt0LkNvcmVCcm93c2VyU2VydmljZT1yfSw4OTM0OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaT10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LG49dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuTW91c2VTZXJ2aWNlPXZvaWQgMDt2YXIgbz1yKDQ3MjUpLHM9cig5ODA2KSxhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQpe3RoaXMuX3JlbmRlclNlcnZpY2U9ZSx0aGlzLl9jaGFyU2l6ZVNlcnZpY2U9dH1yZXR1cm4gZS5wcm90b3R5cGUuZ2V0Q29vcmRzPWZ1bmN0aW9uKGUsdCxyLGksbil7cmV0dXJuKDAscy5nZXRDb29yZHMpKGUsdCxyLGksdGhpcy5fY2hhclNpemVTZXJ2aWNlLmhhc1ZhbGlkU2l6ZSx0aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoLHRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0LG4pfSxlLnByb3RvdHlwZS5nZXRSYXdCeXRlQ29vcmRzPWZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuPXRoaXMuZ2V0Q29vcmRzKGUsdCxyLGkpO3JldHVybigwLHMuZ2V0UmF3Qnl0ZUNvb3Jkcykobil9LGkoW24oMCxvLklSZW5kZXJTZXJ2aWNlKSxuKDEsby5JQ2hhclNpemVTZXJ2aWNlKV0sZSl9KCk7dC5Nb3VzZVNlcnZpY2U9YX0sMzIzMDpmdW5jdGlvbihlLHQscil7dmFyIGksbj10aGlzJiZ0aGlzLl9fZXh0ZW5kc3x8KGk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gaT1PYmplY3Quc2V0UHJvdG90eXBlT2Z8fHtfX3Byb3RvX186W119aW5zdGFuY2VvZiBBcnJheSYmZnVuY3Rpb24oZSx0KXtlLl9fcHJvdG9fXz10fXx8ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHIgaW4gdClPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodCxyKSYmKGVbcl09dFtyXSl9LGkoZSx0KX0sZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgdCYmbnVsbCE9PXQpdGhyb3cgbmV3IFR5cGVFcnJvcigiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSAiK1N0cmluZyh0KSsiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGwiKTtmdW5jdGlvbiByKCl7dGhpcy5jb25zdHJ1Y3Rvcj1lfWkoZSx0KSxlLnByb3RvdHlwZT1udWxsPT09dD9PYmplY3QuY3JlYXRlKHQpOihyLnByb3RvdHlwZT10LnByb3RvdHlwZSxuZXcgcil9KSxvPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30scz10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5SZW5kZXJTZXJ2aWNlPXZvaWQgMDt2YXIgYT1yKDYxOTMpLGM9cig4NDYwKSxsPXIoODQ0KSx1PXIoNTU5NiksaD1yKDM2NTYpLGY9cigyNTg1KSxfPXIoNDcyNSksZD1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpLG4sbyxzKXt2YXIgbD1lLmNhbGwodGhpcyl8fHRoaXM7aWYobC5fcmVuZGVyZXI9dCxsLl9yb3dDb3VudD1yLGwuX2NoYXJTaXplU2VydmljZT1vLGwuX2lzUGF1c2VkPSExLGwuX25lZWRzRnVsbFJlZnJlc2g9ITEsbC5faXNOZXh0UmVuZGVyUmVkcmF3T25seT0hMCxsLl9uZWVkc1NlbGVjdGlvblJlZnJlc2g9ITEsbC5fY2FudmFzV2lkdGg9MCxsLl9jYW52YXNIZWlnaHQ9MCxsLl9zZWxlY3Rpb25TdGF0ZT17c3RhcnQ6dm9pZCAwLGVuZDp2b2lkIDAsY29sdW1uU2VsZWN0TW9kZTohMX0sbC5fb25EaW1lbnNpb25zQ2hhbmdlPW5ldyBjLkV2ZW50RW1pdHRlcixsLl9vblJlbmRlcj1uZXcgYy5FdmVudEVtaXR0ZXIsbC5fb25SZWZyZXNoUmVxdWVzdD1uZXcgYy5FdmVudEVtaXR0ZXIsbC5yZWdpc3Rlcih7ZGlzcG9zZTpmdW5jdGlvbigpe3JldHVybiBsLl9yZW5kZXJlci5kaXNwb3NlKCl9fSksbC5fcmVuZGVyRGVib3VuY2VyPW5ldyBhLlJlbmRlckRlYm91bmNlcigoZnVuY3Rpb24oZSx0KXtyZXR1cm4gbC5fcmVuZGVyUm93cyhlLHQpfSkpLGwucmVnaXN0ZXIobC5fcmVuZGVyRGVib3VuY2VyKSxsLl9zY3JlZW5EcHJNb25pdG9yPW5ldyB1LlNjcmVlbkRwck1vbml0b3IsbC5fc2NyZWVuRHByTW9uaXRvci5zZXRMaXN0ZW5lcigoZnVuY3Rpb24oKXtyZXR1cm4gbC5vbkRldmljZVBpeGVsUmF0aW9DaGFuZ2UoKX0pKSxsLnJlZ2lzdGVyKGwuX3NjcmVlbkRwck1vbml0b3IpLGwucmVnaXN0ZXIocy5vblJlc2l6ZSgoZnVuY3Rpb24oZSl7cmV0dXJuIGwuX2Z1bGxSZWZyZXNoKCl9KSkpLGwucmVnaXN0ZXIobi5vbk9wdGlvbkNoYW5nZSgoZnVuY3Rpb24oKXtyZXR1cm4gbC5fcmVuZGVyZXIub25PcHRpb25zQ2hhbmdlZCgpfSkpKSxsLnJlZ2lzdGVyKGwuX2NoYXJTaXplU2VydmljZS5vbkNoYXJTaXplQ2hhbmdlKChmdW5jdGlvbigpe3JldHVybiBsLm9uQ2hhclNpemVDaGFuZ2VkKCl9KSkpLGwuX3JlbmRlcmVyLm9uUmVxdWVzdFJlZHJhdygoZnVuY3Rpb24oZSl7cmV0dXJuIGwucmVmcmVzaFJvd3MoZS5zdGFydCxlLmVuZCwhMCl9KSksbC5yZWdpc3RlcigoMCxoLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikod2luZG93LCJyZXNpemUiLChmdW5jdGlvbigpe3JldHVybiBsLm9uRGV2aWNlUGl4ZWxSYXRpb0NoYW5nZSgpfSkpKSwiSW50ZXJzZWN0aW9uT2JzZXJ2ZXIiaW4gd2luZG93KXt2YXIgZj1uZXcgSW50ZXJzZWN0aW9uT2JzZXJ2ZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBsLl9vbkludGVyc2VjdGlvbkNoYW5nZShlW2UubGVuZ3RoLTFdKX0pLHt0aHJlc2hvbGQ6MH0pO2Yub2JzZXJ2ZShpKSxsLnJlZ2lzdGVyKHtkaXNwb3NlOmZ1bmN0aW9uKCl7cmV0dXJuIGYuZGlzY29ubmVjdCgpfX0pfXJldHVybiBsfXJldHVybiBuKHQsZSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkRpbWVuc2lvbnNDaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25EaW1lbnNpb25zQ2hhbmdlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZW5kZXJlZEJ1ZmZlckNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlbmRlci5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uUmVmcmVzaFJlcXVlc3QiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZWZyZXNoUmVxdWVzdC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsImRpbWVuc2lvbnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fcmVuZGVyZXIuZGltZW5zaW9uc30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSx0LnByb3RvdHlwZS5fb25JbnRlcnNlY3Rpb25DaGFuZ2U9ZnVuY3Rpb24oZSl7dGhpcy5faXNQYXVzZWQ9dm9pZCAwPT09ZS5pc0ludGVyc2VjdGluZz8wPT09ZS5pbnRlcnNlY3Rpb25SYXRpbzohZS5pc0ludGVyc2VjdGluZyx0aGlzLl9pc1BhdXNlZHx8dGhpcy5fY2hhclNpemVTZXJ2aWNlLmhhc1ZhbGlkU2l6ZXx8dGhpcy5fY2hhclNpemVTZXJ2aWNlLm1lYXN1cmUoKSwhdGhpcy5faXNQYXVzZWQmJnRoaXMuX25lZWRzRnVsbFJlZnJlc2gmJih0aGlzLnJlZnJlc2hSb3dzKDAsdGhpcy5fcm93Q291bnQtMSksdGhpcy5fbmVlZHNGdWxsUmVmcmVzaD0hMSl9LHQucHJvdG90eXBlLnJlZnJlc2hSb3dzPWZ1bmN0aW9uKGUsdCxyKXt2b2lkIDA9PT1yJiYocj0hMSksdGhpcy5faXNQYXVzZWQ/dGhpcy5fbmVlZHNGdWxsUmVmcmVzaD0hMDoocnx8KHRoaXMuX2lzTmV4dFJlbmRlclJlZHJhd09ubHk9ITEpLHRoaXMuX3JlbmRlckRlYm91bmNlci5yZWZyZXNoKGUsdCx0aGlzLl9yb3dDb3VudCkpfSx0LnByb3RvdHlwZS5fcmVuZGVyUm93cz1mdW5jdGlvbihlLHQpe3RoaXMuX3JlbmRlcmVyLnJlbmRlclJvd3MoZSx0KSx0aGlzLl9uZWVkc1NlbGVjdGlvblJlZnJlc2gmJih0aGlzLl9yZW5kZXJlci5vblNlbGVjdGlvbkNoYW5nZWQodGhpcy5fc2VsZWN0aW9uU3RhdGUuc3RhcnQsdGhpcy5fc2VsZWN0aW9uU3RhdGUuZW5kLHRoaXMuX3NlbGVjdGlvblN0YXRlLmNvbHVtblNlbGVjdE1vZGUpLHRoaXMuX25lZWRzU2VsZWN0aW9uUmVmcmVzaD0hMSksdGhpcy5faXNOZXh0UmVuZGVyUmVkcmF3T25seXx8dGhpcy5fb25SZW5kZXIuZmlyZSh7c3RhcnQ6ZSxlbmQ6dH0pLHRoaXMuX2lzTmV4dFJlbmRlclJlZHJhd09ubHk9ITB9LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuX3Jvd0NvdW50PXQsdGhpcy5fZmlyZU9uQ2FudmFzUmVzaXplKCl9LHQucHJvdG90eXBlLmNoYW5nZU9wdGlvbnM9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5vbk9wdGlvbnNDaGFuZ2VkKCksdGhpcy5yZWZyZXNoUm93cygwLHRoaXMuX3Jvd0NvdW50LTEpLHRoaXMuX2ZpcmVPbkNhbnZhc1Jlc2l6ZSgpfSx0LnByb3RvdHlwZS5fZmlyZU9uQ2FudmFzUmVzaXplPWZ1bmN0aW9uKCl7dGhpcy5fcmVuZGVyZXIuZGltZW5zaW9ucy5jYW52YXNXaWR0aD09PXRoaXMuX2NhbnZhc1dpZHRoJiZ0aGlzLl9yZW5kZXJlci5kaW1lbnNpb25zLmNhbnZhc0hlaWdodD09PXRoaXMuX2NhbnZhc0hlaWdodHx8dGhpcy5fb25EaW1lbnNpb25zQ2hhbmdlLmZpcmUodGhpcy5fcmVuZGVyZXIuZGltZW5zaW9ucyl9LHQucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXtlLnByb3RvdHlwZS5kaXNwb3NlLmNhbGwodGhpcyl9LHQucHJvdG90eXBlLnNldFJlbmRlcmVyPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7dGhpcy5fcmVuZGVyZXIuZGlzcG9zZSgpLHRoaXMuX3JlbmRlcmVyPWUsdGhpcy5fcmVuZGVyZXIub25SZXF1ZXN0UmVkcmF3KChmdW5jdGlvbihlKXtyZXR1cm4gdC5yZWZyZXNoUm93cyhlLnN0YXJ0LGUuZW5kLCEwKX0pKSx0aGlzLl9uZWVkc1NlbGVjdGlvblJlZnJlc2g9ITAsdGhpcy5fZnVsbFJlZnJlc2goKX0sdC5wcm90b3R5cGUuX2Z1bGxSZWZyZXNoPWZ1bmN0aW9uKCl7dGhpcy5faXNQYXVzZWQ/dGhpcy5fbmVlZHNGdWxsUmVmcmVzaD0hMDp0aGlzLnJlZnJlc2hSb3dzKDAsdGhpcy5fcm93Q291bnQtMSl9LHQucHJvdG90eXBlLmNsZWFyVGV4dHVyZUF0bGFzPWZ1bmN0aW9uKCl7dmFyIGUsdDtudWxsPT09KHQ9bnVsbD09PShlPXRoaXMuX3JlbmRlcmVyKXx8dm9pZCAwPT09ZT92b2lkIDA6ZS5jbGVhclRleHR1cmVBdGxhcyl8fHZvaWQgMD09PXR8fHQuY2FsbChlKSx0aGlzLl9mdWxsUmVmcmVzaCgpfSx0LnByb3RvdHlwZS5zZXRDb2xvcnM9ZnVuY3Rpb24oZSl7dGhpcy5fcmVuZGVyZXIuc2V0Q29sb3JzKGUpLHRoaXMuX2Z1bGxSZWZyZXNoKCl9LHQucHJvdG90eXBlLm9uRGV2aWNlUGl4ZWxSYXRpb0NoYW5nZT1mdW5jdGlvbigpe3RoaXMuX2NoYXJTaXplU2VydmljZS5tZWFzdXJlKCksdGhpcy5fcmVuZGVyZXIub25EZXZpY2VQaXhlbFJhdGlvQ2hhbmdlKCksdGhpcy5yZWZyZXNoUm93cygwLHRoaXMuX3Jvd0NvdW50LTEpfSx0LnByb3RvdHlwZS5vblJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuX3JlbmRlcmVyLm9uUmVzaXplKGUsdCksdGhpcy5fZnVsbFJlZnJlc2goKX0sdC5wcm90b3R5cGUub25DaGFyU2l6ZUNoYW5nZWQ9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5vbkNoYXJTaXplQ2hhbmdlZCgpfSx0LnByb3RvdHlwZS5vbkJsdXI9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5vbkJsdXIoKX0sdC5wcm90b3R5cGUub25Gb2N1cz1mdW5jdGlvbigpe3RoaXMuX3JlbmRlcmVyLm9uRm9jdXMoKX0sdC5wcm90b3R5cGUub25TZWxlY3Rpb25DaGFuZ2VkPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9zZWxlY3Rpb25TdGF0ZS5zdGFydD1lLHRoaXMuX3NlbGVjdGlvblN0YXRlLmVuZD10LHRoaXMuX3NlbGVjdGlvblN0YXRlLmNvbHVtblNlbGVjdE1vZGU9cix0aGlzLl9yZW5kZXJlci5vblNlbGVjdGlvbkNoYW5nZWQoZSx0LHIpfSx0LnByb3RvdHlwZS5vbkN1cnNvck1vdmU9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5vbkN1cnNvck1vdmUoKX0sdC5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5jbGVhcigpfSxvKFtzKDMsZi5JT3B0aW9uc1NlcnZpY2UpLHMoNCxfLklDaGFyU2l6ZVNlcnZpY2UpLHMoNSxmLklCdWZmZXJTZXJ2aWNlKV0sdCl9KGwuRGlzcG9zYWJsZSk7dC5SZW5kZXJTZXJ2aWNlPWR9LDkzMTI6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSksbz10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LHM9dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuU2VsZWN0aW9uU2VydmljZT12b2lkIDA7dmFyIGE9cig2MTE0KSxjPXIoNDU2KSxsPXIoNTExKSx1PXIoODQ2MCksaD1yKDQ3MjUpLGY9cigyNTg1KSxfPXIoOTgwNiksZD1yKDk1MDQpLHA9cig4NDQpLHY9cig0ODQxKSxnPVN0cmluZy5mcm9tQ2hhckNvZGUoMTYwKSx5PW5ldyBSZWdFeHAoZywiZyIpLG09ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8scyxhLGgpe3ZhciBmPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gZi5fZWxlbWVudD10LGYuX3NjcmVlbkVsZW1lbnQ9cixmLl9saW5raWZpZXI9aSxmLl9idWZmZXJTZXJ2aWNlPW4sZi5fY29yZVNlcnZpY2U9byxmLl9tb3VzZVNlcnZpY2U9cyxmLl9vcHRpb25zU2VydmljZT1hLGYuX3JlbmRlclNlcnZpY2U9aCxmLl9kcmFnU2Nyb2xsQW1vdW50PTAsZi5fZW5hYmxlZD0hMCxmLl93b3JrQ2VsbD1uZXcgbC5DZWxsRGF0YSxmLl9tb3VzZURvd25UaW1lU3RhbXA9MCxmLl9vbGRIYXNTZWxlY3Rpb249ITEsZi5fb2xkU2VsZWN0aW9uU3RhcnQ9dm9pZCAwLGYuX29sZFNlbGVjdGlvbkVuZD12b2lkIDAsZi5fb25MaW51eE1vdXNlU2VsZWN0aW9uPWYucmVnaXN0ZXIobmV3IHUuRXZlbnRFbWl0dGVyKSxmLl9vblJlZHJhd1JlcXVlc3Q9Zi5yZWdpc3RlcihuZXcgdS5FdmVudEVtaXR0ZXIpLGYuX29uU2VsZWN0aW9uQ2hhbmdlPWYucmVnaXN0ZXIobmV3IHUuRXZlbnRFbWl0dGVyKSxmLl9vblJlcXVlc3RTY3JvbGxMaW5lcz1mLnJlZ2lzdGVyKG5ldyB1LkV2ZW50RW1pdHRlciksZi5fbW91c2VNb3ZlTGlzdGVuZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIGYuX29uTW91c2VNb3ZlKGUpfSxmLl9tb3VzZVVwTGlzdGVuZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIGYuX29uTW91c2VVcChlKX0sZi5fY29yZVNlcnZpY2Uub25Vc2VySW5wdXQoKGZ1bmN0aW9uKCl7Zi5oYXNTZWxlY3Rpb24mJmYuY2xlYXJTZWxlY3Rpb24oKX0pKSxmLl90cmltTGlzdGVuZXI9Zi5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMub25UcmltKChmdW5jdGlvbihlKXtyZXR1cm4gZi5fb25UcmltKGUpfSkpLGYucmVnaXN0ZXIoZi5fYnVmZmVyU2VydmljZS5idWZmZXJzLm9uQnVmZmVyQWN0aXZhdGUoKGZ1bmN0aW9uKGUpe3JldHVybiBmLl9vbkJ1ZmZlckFjdGl2YXRlKGUpfSkpKSxmLmVuYWJsZSgpLGYuX21vZGVsPW5ldyBjLlNlbGVjdGlvbk1vZGVsKGYuX2J1ZmZlclNlcnZpY2UpLGYuX2FjdGl2ZVNlbGVjdGlvbk1vZGU9MCxmfXJldHVybiBuKHQsZSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkxpbnV4TW91c2VTZWxlY3Rpb24iLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25MaW51eE1vdXNlU2VsZWN0aW9uLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0UmVkcmF3Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uUmVkcmF3UmVxdWVzdC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uU2VsZWN0aW9uQ2hhbmdlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uU2VsZWN0aW9uQ2hhbmdlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0U2Nyb2xsTGluZXMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZXF1ZXN0U2Nyb2xsTGluZXMuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3RoaXMuX3JlbW92ZU1vdXNlRG93bkxpc3RlbmVycygpfSx0LnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe3RoaXMuY2xlYXJTZWxlY3Rpb24oKX0sdC5wcm90b3R5cGUuZGlzYWJsZT1mdW5jdGlvbigpe3RoaXMuY2xlYXJTZWxlY3Rpb24oKSx0aGlzLl9lbmFibGVkPSExfSx0LnByb3RvdHlwZS5lbmFibGU9ZnVuY3Rpb24oKXt0aGlzLl9lbmFibGVkPSEwfSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsInNlbGVjdGlvblN0YXJ0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uU3RhcnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJzZWxlY3Rpb25FbmQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fbW9kZWwuZmluYWxTZWxlY3Rpb25FbmR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJoYXNTZWxlY3Rpb24iLHtnZXQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLl9tb2RlbC5maW5hbFNlbGVjdGlvblN0YXJ0LHQ9dGhpcy5fbW9kZWwuZmluYWxTZWxlY3Rpb25FbmQ7cmV0dXJuISghZXx8IXR8fGVbMF09PT10WzBdJiZlWzFdPT09dFsxXSl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJzZWxlY3Rpb25UZXh0Iix7Z2V0OmZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5fbW9kZWwuZmluYWxTZWxlY3Rpb25TdGFydCx0PXRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uRW5kO2lmKCFlfHwhdClyZXR1cm4iIjt2YXIgcj10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlcixpPVtdO2lmKDM9PT10aGlzLl9hY3RpdmVTZWxlY3Rpb25Nb2RlKXtpZihlWzBdPT09dFswXSlyZXR1cm4iIjtmb3IodmFyIG49ZVsxXTtuPD10WzFdO24rKyl7dmFyIG89ci50cmFuc2xhdGVCdWZmZXJMaW5lVG9TdHJpbmcobiwhMCxlWzBdLHRbMF0pO2kucHVzaChvKX19ZWxzZXt2YXIgcz1lWzFdPT09dFsxXT90WzBdOnZvaWQgMDtmb3IoaS5wdXNoKHIudHJhbnNsYXRlQnVmZmVyTGluZVRvU3RyaW5nKGVbMV0sITAsZVswXSxzKSksbj1lWzFdKzE7bjw9dFsxXS0xO24rKyl7dmFyIGM9ci5saW5lcy5nZXQobik7bz1yLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhuLCEwKSwobnVsbD09Yz92b2lkIDA6Yy5pc1dyYXBwZWQpP2lbaS5sZW5ndGgtMV0rPW86aS5wdXNoKG8pfWVbMV0hPT10WzFdJiYoYz1yLmxpbmVzLmdldCh0WzFdKSxvPXIudHJhbnNsYXRlQnVmZmVyTGluZVRvU3RyaW5nKHRbMV0sITAsMCx0WzBdKSxjJiZjLmlzV3JhcHBlZD9pW2kubGVuZ3RoLTFdKz1vOmkucHVzaChvKSl9cmV0dXJuIGkubWFwKChmdW5jdGlvbihlKXtyZXR1cm4gZS5yZXBsYWNlKHksIiAiKX0pKS5qb2luKGEuaXNXaW5kb3dzPyJcclxuIjoiXG4iKX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSx0LnByb3RvdHlwZS5jbGVhclNlbGVjdGlvbj1mdW5jdGlvbigpe3RoaXMuX21vZGVsLmNsZWFyU2VsZWN0aW9uKCksdGhpcy5fcmVtb3ZlTW91c2VEb3duTGlzdGVuZXJzKCksdGhpcy5yZWZyZXNoKCksdGhpcy5fb25TZWxlY3Rpb25DaGFuZ2UuZmlyZSgpfSx0LnByb3RvdHlwZS5yZWZyZXNoPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7dGhpcy5fcmVmcmVzaEFuaW1hdGlvbkZyYW1lfHwodGhpcy5fcmVmcmVzaEFuaW1hdGlvbkZyYW1lPXdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKGZ1bmN0aW9uKCl7cmV0dXJuIHQuX3JlZnJlc2goKX0pKSksYS5pc0xpbnV4JiZlJiZ0aGlzLnNlbGVjdGlvblRleHQubGVuZ3RoJiZ0aGlzLl9vbkxpbnV4TW91c2VTZWxlY3Rpb24uZmlyZSh0aGlzLnNlbGVjdGlvblRleHQpfSx0LnByb3RvdHlwZS5fcmVmcmVzaD1mdW5jdGlvbigpe3RoaXMuX3JlZnJlc2hBbmltYXRpb25GcmFtZT12b2lkIDAsdGhpcy5fb25SZWRyYXdSZXF1ZXN0LmZpcmUoe3N0YXJ0OnRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uU3RhcnQsZW5kOnRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uRW5kLGNvbHVtblNlbGVjdE1vZGU6Mz09PXRoaXMuX2FjdGl2ZVNlbGVjdGlvbk1vZGV9KX0sdC5wcm90b3R5cGUuX2lzQ2xpY2tJblNlbGVjdGlvbj1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9nZXRNb3VzZUJ1ZmZlckNvb3JkcyhlKSxyPXRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uU3RhcnQsaT10aGlzLl9tb2RlbC5maW5hbFNlbGVjdGlvbkVuZDtyZXR1cm4hIShyJiZpJiZ0KSYmdGhpcy5fYXJlQ29vcmRzSW5TZWxlY3Rpb24odCxyLGkpfSx0LnByb3RvdHlwZS5fYXJlQ29vcmRzSW5TZWxlY3Rpb249ZnVuY3Rpb24oZSx0LHIpe3JldHVybiBlWzFdPnRbMV0mJmVbMV08clsxXXx8dFsxXT09PXJbMV0mJmVbMV09PT10WzFdJiZlWzBdPj10WzBdJiZlWzBdPHJbMF18fHRbMV08clsxXSYmZVsxXT09PXJbMV0mJmVbMF08clswXXx8dFsxXTxyWzFdJiZlWzFdPT09dFsxXSYmZVswXT49dFswXX0sdC5wcm90b3R5cGUuX3NlbGVjdFdvcmRBdEN1cnNvcj1mdW5jdGlvbihlLHQpe3ZhciByLGksbj1udWxsPT09KGk9bnVsbD09PShyPXRoaXMuX2xpbmtpZmllci5jdXJyZW50TGluayl8fHZvaWQgMD09PXI/dm9pZCAwOnIubGluayl8fHZvaWQgMD09PWk/dm9pZCAwOmkucmFuZ2U7aWYobilyZXR1cm4gdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQ9W24uc3RhcnQueC0xLG4uc3RhcnQueS0xXSx0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydExlbmd0aD0oMCx2LmdldFJhbmdlTGVuZ3RoKShuLHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyksdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kPXZvaWQgMCwhMDt2YXIgbz10aGlzLl9nZXRNb3VzZUJ1ZmZlckNvb3JkcyhlKTtyZXR1cm4hIW8mJih0aGlzLl9zZWxlY3RXb3JkQXQobyx0KSx0aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmQ9dm9pZCAwLCEwKX0sdC5wcm90b3R5cGUuc2VsZWN0QWxsPWZ1bmN0aW9uKCl7dGhpcy5fbW9kZWwuaXNTZWxlY3RBbGxBY3RpdmU9ITAsdGhpcy5yZWZyZXNoKCksdGhpcy5fb25TZWxlY3Rpb25DaGFuZ2UuZmlyZSgpfSx0LnByb3RvdHlwZS5zZWxlY3RMaW5lcz1mdW5jdGlvbihlLHQpe3RoaXMuX21vZGVsLmNsZWFyU2VsZWN0aW9uKCksZT1NYXRoLm1heChlLDApLHQ9TWF0aC5taW4odCx0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci5saW5lcy5sZW5ndGgtMSksdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQ9WzAsZV0sdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kPVt0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdF0sdGhpcy5yZWZyZXNoKCksdGhpcy5fb25TZWxlY3Rpb25DaGFuZ2UuZmlyZSgpfSx0LnByb3RvdHlwZS5fb25UcmltPWZ1bmN0aW9uKGUpe3RoaXMuX21vZGVsLm9uVHJpbShlKSYmdGhpcy5yZWZyZXNoKCl9LHQucHJvdG90eXBlLl9nZXRNb3VzZUJ1ZmZlckNvb3Jkcz1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9tb3VzZVNlcnZpY2UuZ2V0Q29vcmRzKGUsdGhpcy5fc2NyZWVuRWxlbWVudCx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLCEwKTtpZih0KXJldHVybiB0WzBdLS0sdFsxXS0tLHRbMV0rPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwLHR9LHQucHJvdG90eXBlLl9nZXRNb3VzZUV2ZW50U2Nyb2xsQW1vdW50PWZ1bmN0aW9uKGUpe3ZhciB0PSgwLF8uZ2V0Q29vcmRzUmVsYXRpdmVUb0VsZW1lbnQpKGUsdGhpcy5fc2NyZWVuRWxlbWVudClbMV0scj10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0O3JldHVybiB0Pj0wJiZ0PD1yPzA6KHQ+ciYmKHQtPXIpLHQ9TWF0aC5taW4oTWF0aC5tYXgodCwtNTApLDUwKSwodC89NTApL01hdGguYWJzKHQpK01hdGgucm91bmQoMTQqdCkpfSx0LnByb3RvdHlwZS5zaG91bGRGb3JjZVNlbGVjdGlvbj1mdW5jdGlvbihlKXtyZXR1cm4gYS5pc01hYz9lLmFsdEtleSYmdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5tYWNPcHRpb25DbGlja0ZvcmNlc1NlbGVjdGlvbjplLnNoaWZ0S2V5fSx0LnByb3RvdHlwZS5vbk1vdXNlRG93bj1mdW5jdGlvbihlKXtpZih0aGlzLl9tb3VzZURvd25UaW1lU3RhbXA9ZS50aW1lU3RhbXAsKDIhPT1lLmJ1dHRvbnx8IXRoaXMuaGFzU2VsZWN0aW9uKSYmMD09PWUuYnV0dG9uKXtpZighdGhpcy5fZW5hYmxlZCl7aWYoIXRoaXMuc2hvdWxkRm9yY2VTZWxlY3Rpb24oZSkpcmV0dXJuO2Uuc3RvcFByb3BhZ2F0aW9uKCl9ZS5wcmV2ZW50RGVmYXVsdCgpLHRoaXMuX2RyYWdTY3JvbGxBbW91bnQ9MCx0aGlzLl9lbmFibGVkJiZlLnNoaWZ0S2V5P3RoaXMuX29uSW5jcmVtZW50YWxDbGljayhlKToxPT09ZS5kZXRhaWw/dGhpcy5fb25TaW5nbGVDbGljayhlKToyPT09ZS5kZXRhaWw/dGhpcy5fb25Eb3VibGVDbGljayhlKTozPT09ZS5kZXRhaWwmJnRoaXMuX29uVHJpcGxlQ2xpY2soZSksdGhpcy5fYWRkTW91c2VEb3duTGlzdGVuZXJzKCksdGhpcy5yZWZyZXNoKCEwKX19LHQucHJvdG90eXBlLl9hZGRNb3VzZURvd25MaXN0ZW5lcnM9ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3RoaXMuX3NjcmVlbkVsZW1lbnQub3duZXJEb2N1bWVudCYmKHRoaXMuX3NjcmVlbkVsZW1lbnQub3duZXJEb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCJtb3VzZW1vdmUiLHRoaXMuX21vdXNlTW92ZUxpc3RlbmVyKSx0aGlzLl9zY3JlZW5FbGVtZW50Lm93bmVyRG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigibW91c2V1cCIsdGhpcy5fbW91c2VVcExpc3RlbmVyKSksdGhpcy5fZHJhZ1Njcm9sbEludGVydmFsVGltZXI9d2luZG93LnNldEludGVydmFsKChmdW5jdGlvbigpe3JldHVybiBlLl9kcmFnU2Nyb2xsKCl9KSw1MCl9LHQucHJvdG90eXBlLl9yZW1vdmVNb3VzZURvd25MaXN0ZW5lcnM9ZnVuY3Rpb24oKXt0aGlzLl9zY3JlZW5FbGVtZW50Lm93bmVyRG9jdW1lbnQmJih0aGlzLl9zY3JlZW5FbGVtZW50Lm93bmVyRG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2Vtb3ZlIix0aGlzLl9tb3VzZU1vdmVMaXN0ZW5lciksdGhpcy5fc2NyZWVuRWxlbWVudC5vd25lckRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoIm1vdXNldXAiLHRoaXMuX21vdXNlVXBMaXN0ZW5lcikpLGNsZWFySW50ZXJ2YWwodGhpcy5fZHJhZ1Njcm9sbEludGVydmFsVGltZXIpLHRoaXMuX2RyYWdTY3JvbGxJbnRlcnZhbFRpbWVyPXZvaWQgMH0sdC5wcm90b3R5cGUuX29uSW5jcmVtZW50YWxDbGljaz1mdW5jdGlvbihlKXt0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydCYmKHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZD10aGlzLl9nZXRNb3VzZUJ1ZmZlckNvb3JkcyhlKSl9LHQucHJvdG90eXBlLl9vblNpbmdsZUNsaWNrPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0TGVuZ3RoPTAsdGhpcy5fbW9kZWwuaXNTZWxlY3RBbGxBY3RpdmU9ITEsdGhpcy5fYWN0aXZlU2VsZWN0aW9uTW9kZT10aGlzLnNob3VsZENvbHVtblNlbGVjdChlKT8zOjAsdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQ9dGhpcy5fZ2V0TW91c2VCdWZmZXJDb29yZHMoZSksdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQpe3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZD12b2lkIDA7dmFyIHQ9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0WzFdKTt0JiZ0Lmxlbmd0aCE9PXRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0WzBdJiYwPT09dC5oYXNXaWR0aCh0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydFswXSkmJnRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0WzBdKyt9fSx0LnByb3RvdHlwZS5fb25Eb3VibGVDbGljaz1mdW5jdGlvbihlKXt0aGlzLl9zZWxlY3RXb3JkQXRDdXJzb3IoZSwhMCkmJih0aGlzLl9hY3RpdmVTZWxlY3Rpb25Nb2RlPTEpfSx0LnByb3RvdHlwZS5fb25UcmlwbGVDbGljaz1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9nZXRNb3VzZUJ1ZmZlckNvb3JkcyhlKTt0JiYodGhpcy5fYWN0aXZlU2VsZWN0aW9uTW9kZT0yLHRoaXMuX3NlbGVjdExpbmVBdCh0WzFdKSl9LHQucHJvdG90eXBlLnNob3VsZENvbHVtblNlbGVjdD1mdW5jdGlvbihlKXtyZXR1cm4gZS5hbHRLZXkmJiEoYS5pc01hYyYmdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5tYWNPcHRpb25DbGlja0ZvcmNlc1NlbGVjdGlvbil9LHQucHJvdG90eXBlLl9vbk1vdXNlTW92ZT1mdW5jdGlvbihlKXtpZihlLnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpLHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0KXt2YXIgdD10aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmQ/W3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXSx0aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmRbMV1dOm51bGw7aWYodGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kPXRoaXMuX2dldE1vdXNlQnVmZmVyQ29vcmRzKGUpLHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZCl7Mj09PXRoaXMuX2FjdGl2ZVNlbGVjdGlvbk1vZGU/dGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzFdPHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0WzFdP3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXT0wOnRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM6MT09PXRoaXMuX2FjdGl2ZVNlbGVjdGlvbk1vZGUmJnRoaXMuX3NlbGVjdFRvV29yZEF0KHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZCksdGhpcy5fZHJhZ1Njcm9sbEFtb3VudD10aGlzLl9nZXRNb3VzZUV2ZW50U2Nyb2xsQW1vdW50KGUpLDMhPT10aGlzLl9hY3RpdmVTZWxlY3Rpb25Nb2RlJiYodGhpcy5fZHJhZ1Njcm9sbEFtb3VudD4wP3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM6dGhpcy5fZHJhZ1Njcm9sbEFtb3VudDwwJiYodGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzBdPTApKTt2YXIgcj10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlcjtpZih0aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmRbMV08ci5saW5lcy5sZW5ndGgpe3ZhciBpPXIubGluZXMuZ2V0KHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFsxXSk7aSYmMD09PWkuaGFzV2lkdGgodGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzBdKSYmdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzBdKyt9dCYmdFswXT09PXRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXSYmdFsxXT09PXRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFsxXXx8dGhpcy5yZWZyZXNoKCEwKX1lbHNlIHRoaXMucmVmcmVzaCghMCl9fSx0LnByb3RvdHlwZS5fZHJhZ1Njcm9sbD1mdW5jdGlvbigpe2lmKHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZCYmdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQmJnRoaXMuX2RyYWdTY3JvbGxBbW91bnQpe3RoaXMuX29uUmVxdWVzdFNjcm9sbExpbmVzLmZpcmUoe2Ftb3VudDp0aGlzLl9kcmFnU2Nyb2xsQW1vdW50LHN1cHByZXNzU2Nyb2xsRXZlbnQ6ITF9KTt2YXIgZT10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlcjt0aGlzLl9kcmFnU2Nyb2xsQW1vdW50PjA/KDMhPT10aGlzLl9hY3RpdmVTZWxlY3Rpb25Nb2RlJiYodGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzBdPXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyksdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzFdPU1hdGgubWluKGUueWRpc3ArdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLGUubGluZXMubGVuZ3RoLTEpKTooMyE9PXRoaXMuX2FjdGl2ZVNlbGVjdGlvbk1vZGUmJih0aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmRbMF09MCksdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzFdPWUueWRpc3ApLHRoaXMucmVmcmVzaCgpfX0sdC5wcm90b3R5cGUuX29uTW91c2VVcD1mdW5jdGlvbihlKXt2YXIgdD1lLnRpbWVTdGFtcC10aGlzLl9tb3VzZURvd25UaW1lU3RhbXA7aWYodGhpcy5fcmVtb3ZlTW91c2VEb3duTGlzdGVuZXJzKCksdGhpcy5zZWxlY3Rpb25UZXh0Lmxlbmd0aDw9MSYmdDw1MDAmJmUuYWx0S2V5JiZ0aGlzLl9vcHRpb25zU2VydmljZS5nZXRPcHRpb24oImFsdENsaWNrTW92ZXNDdXJzb3IiKSl7aWYodGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWJhc2U9PT10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCl7dmFyIHI9dGhpcy5fbW91c2VTZXJ2aWNlLmdldENvb3JkcyhlLHRoaXMuX2VsZW1lbnQsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cywhMSk7aWYociYmdm9pZCAwIT09clswXSYmdm9pZCAwIT09clsxXSl7dmFyIGk9KDAsZC5tb3ZlVG9DZWxsU2VxdWVuY2UpKHJbMF0tMSxyWzFdLTEsdGhpcy5fYnVmZmVyU2VydmljZSx0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuYXBwbGljYXRpb25DdXJzb3JLZXlzKTt0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KGksITApfX19ZWxzZSB0aGlzLl9maXJlRXZlbnRJZlNlbGVjdGlvbkNoYW5nZWQoKX0sdC5wcm90b3R5cGUuX2ZpcmVFdmVudElmU2VsZWN0aW9uQ2hhbmdlZD1mdW5jdGlvbigpe3ZhciBlPXRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uU3RhcnQsdD10aGlzLl9tb2RlbC5maW5hbFNlbGVjdGlvbkVuZCxyPSEoIWV8fCF0fHxlWzBdPT09dFswXSYmZVsxXT09PXRbMV0pO3I/ZSYmdCYmKHRoaXMuX29sZFNlbGVjdGlvblN0YXJ0JiZ0aGlzLl9vbGRTZWxlY3Rpb25FbmQmJmVbMF09PT10aGlzLl9vbGRTZWxlY3Rpb25TdGFydFswXSYmZVsxXT09PXRoaXMuX29sZFNlbGVjdGlvblN0YXJ0WzFdJiZ0WzBdPT09dGhpcy5fb2xkU2VsZWN0aW9uRW5kWzBdJiZ0WzFdPT09dGhpcy5fb2xkU2VsZWN0aW9uRW5kWzFdfHx0aGlzLl9maXJlT25TZWxlY3Rpb25DaGFuZ2UoZSx0LHIpKTp0aGlzLl9vbGRIYXNTZWxlY3Rpb24mJnRoaXMuX2ZpcmVPblNlbGVjdGlvbkNoYW5nZShlLHQscil9LHQucHJvdG90eXBlLl9maXJlT25TZWxlY3Rpb25DaGFuZ2U9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX29sZFNlbGVjdGlvblN0YXJ0PWUsdGhpcy5fb2xkU2VsZWN0aW9uRW5kPXQsdGhpcy5fb2xkSGFzU2VsZWN0aW9uPXIsdGhpcy5fb25TZWxlY3Rpb25DaGFuZ2UuZmlyZSgpfSx0LnByb3RvdHlwZS5fb25CdWZmZXJBY3RpdmF0ZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzO3RoaXMuY2xlYXJTZWxlY3Rpb24oKSx0aGlzLl90cmltTGlzdGVuZXIuZGlzcG9zZSgpLHRoaXMuX3RyaW1MaXN0ZW5lcj1lLmFjdGl2ZUJ1ZmZlci5saW5lcy5vblRyaW0oKGZ1bmN0aW9uKGUpe3JldHVybiB0Ll9vblRyaW0oZSl9KSl9LHQucHJvdG90eXBlLl9jb252ZXJ0Vmlld3BvcnRDb2xUb0NoYXJhY3RlckluZGV4PWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRbMF0saT0wO3RbMF0+PWk7aSsrKXt2YXIgbj1lLmxvYWRDZWxsKGksdGhpcy5fd29ya0NlbGwpLmdldENoYXJzKCkubGVuZ3RoOzA9PT10aGlzLl93b3JrQ2VsbC5nZXRXaWR0aCgpP3ItLTpuPjEmJnRbMF0hPT1pJiYocis9bi0xKX1yZXR1cm4gcn0sdC5wcm90b3R5cGUuc2V0U2VsZWN0aW9uPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9tb2RlbC5jbGVhclNlbGVjdGlvbigpLHRoaXMuX3JlbW92ZU1vdXNlRG93bkxpc3RlbmVycygpLHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0PVtlLHRdLHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0TGVuZ3RoPXIsdGhpcy5yZWZyZXNoKCl9LHQucHJvdG90eXBlLnJpZ2h0Q2xpY2tTZWxlY3Q9ZnVuY3Rpb24oZSl7dGhpcy5faXNDbGlja0luU2VsZWN0aW9uKGUpfHwodGhpcy5fc2VsZWN0V29yZEF0Q3Vyc29yKGUsITEpJiZ0aGlzLnJlZnJlc2goITApLHRoaXMuX2ZpcmVFdmVudElmU2VsZWN0aW9uQ2hhbmdlZCgpKX0sdC5wcm90b3R5cGUuX2dldFdvcmRBdD1mdW5jdGlvbihlLHQscixpKXtpZih2b2lkIDA9PT1yJiYocj0hMCksdm9pZCAwPT09aSYmKGk9ITApLCEoZVswXT49dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSl7dmFyIG49dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIsbz1uLmxpbmVzLmdldChlWzFdKTtpZihvKXt2YXIgcz1uLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhlWzFdLCExKSxhPXRoaXMuX2NvbnZlcnRWaWV3cG9ydENvbFRvQ2hhcmFjdGVySW5kZXgobyxlKSxjPWEsbD1lWzBdLWEsdT0wLGg9MCxmPTAsXz0wO2lmKCIgIj09PXMuY2hhckF0KGEpKXtmb3IoO2E+MCYmIiAiPT09cy5jaGFyQXQoYS0xKTspYS0tO2Zvcig7YzxzLmxlbmd0aCYmIiAiPT09cy5jaGFyQXQoYysxKTspYysrfWVsc2V7dmFyIGQ9ZVswXSxwPWVbMF07MD09PW8uZ2V0V2lkdGgoZCkmJih1KyssZC0tKSwyPT09by5nZXRXaWR0aChwKSYmKGgrKyxwKyspO3ZhciB2PW8uZ2V0U3RyaW5nKHApLmxlbmd0aDtmb3Iodj4xJiYoXys9di0xLGMrPXYtMSk7ZD4wJiZhPjAmJiF0aGlzLl9pc0NoYXJXb3JkU2VwYXJhdG9yKG8ubG9hZENlbGwoZC0xLHRoaXMuX3dvcmtDZWxsKSk7KXtvLmxvYWRDZWxsKGQtMSx0aGlzLl93b3JrQ2VsbCk7dmFyIGc9dGhpcy5fd29ya0NlbGwuZ2V0Q2hhcnMoKS5sZW5ndGg7MD09PXRoaXMuX3dvcmtDZWxsLmdldFdpZHRoKCk/KHUrKyxkLS0pOmc+MSYmKGYrPWctMSxhLT1nLTEpLGEtLSxkLS19Zm9yKDtwPG8ubGVuZ3RoJiZjKzE8cy5sZW5ndGgmJiF0aGlzLl9pc0NoYXJXb3JkU2VwYXJhdG9yKG8ubG9hZENlbGwocCsxLHRoaXMuX3dvcmtDZWxsKSk7KXtvLmxvYWRDZWxsKHArMSx0aGlzLl93b3JrQ2VsbCk7dmFyIHk9dGhpcy5fd29ya0NlbGwuZ2V0Q2hhcnMoKS5sZW5ndGg7Mj09PXRoaXMuX3dvcmtDZWxsLmdldFdpZHRoKCk/KGgrKyxwKyspOnk+MSYmKF8rPXktMSxjKz15LTEpLGMrKyxwKyt9fWMrKzt2YXIgbT1hK2wtdStmLGI9TWF0aC5taW4odGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGMtYSt1K2gtZi1fKTtpZih0fHwiIiE9PXMuc2xpY2UoYSxjKS50cmltKCkpe2lmKHImJjA9PT1tJiYzMiE9PW8uZ2V0Q29kZVBvaW50KDApKXt2YXIgUz1uLmxpbmVzLmdldChlWzFdLTEpO2lmKFMmJm8uaXNXcmFwcGVkJiYzMiE9PVMuZ2V0Q29kZVBvaW50KHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scy0xKSl7dmFyIEM9dGhpcy5fZ2V0V29yZEF0KFt0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtMSxlWzFdLTFdLCExLCEwLCExKTtpZihDKXt2YXIgdz10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtQy5zdGFydDttLT13LGIrPXd9fX1pZihpJiZtK2I9PT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMmJjMyIT09by5nZXRDb2RlUG9pbnQodGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLTEpKXt2YXIgTD1uLmxpbmVzLmdldChlWzFdKzEpO2lmKChudWxsPT1MP3ZvaWQgMDpMLmlzV3JhcHBlZCkmJjMyIT09TC5nZXRDb2RlUG9pbnQoMCkpe3ZhciBFPXRoaXMuX2dldFdvcmRBdChbMCxlWzFdKzFdLCExLCExLCEwKTtFJiYoYis9RS5sZW5ndGgpfX1yZXR1cm57c3RhcnQ6bSxsZW5ndGg6Yn19fX19LHQucHJvdG90eXBlLl9zZWxlY3RXb3JkQXQ9ZnVuY3Rpb24oZSx0KXt2YXIgcj10aGlzLl9nZXRXb3JkQXQoZSx0KTtpZihyKXtmb3IoO3Iuc3RhcnQ8MDspci5zdGFydCs9dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGVbMV0tLTt0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydD1bci5zdGFydCxlWzFdXSx0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydExlbmd0aD1yLmxlbmd0aH19LHQucHJvdG90eXBlLl9zZWxlY3RUb1dvcmRBdD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9nZXRXb3JkQXQoZSwhMCk7aWYodCl7Zm9yKHZhciByPWVbMV07dC5zdGFydDwwOyl0LnN0YXJ0Kz10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsci0tO2lmKCF0aGlzLl9tb2RlbC5hcmVTZWxlY3Rpb25WYWx1ZXNSZXZlcnNlZCgpKWZvcig7dC5zdGFydCt0Lmxlbmd0aD50aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM7KXQubGVuZ3RoLT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMscisrO3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZD1bdGhpcy5fbW9kZWwuYXJlU2VsZWN0aW9uVmFsdWVzUmV2ZXJzZWQoKT90LnN0YXJ0OnQuc3RhcnQrdC5sZW5ndGgscl19fSx0LnByb3RvdHlwZS5faXNDaGFyV29yZFNlcGFyYXRvcj1mdW5jdGlvbihlKXtyZXR1cm4gMCE9PWUuZ2V0V2lkdGgoKSYmdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy53b3JkU2VwYXJhdG9yLmluZGV4T2YoZS5nZXRDaGFycygpKT49MH0sdC5wcm90b3R5cGUuX3NlbGVjdExpbmVBdD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci5nZXRXcmFwcGVkUmFuZ2VGb3JMaW5lKGUpO3RoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0PVswLHQuZmlyc3RdLHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZD1bdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLHQubGFzdF0sdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnRMZW5ndGg9MH0sbyhbcygzLGYuSUJ1ZmZlclNlcnZpY2UpLHMoNCxmLklDb3JlU2VydmljZSkscyg1LGguSU1vdXNlU2VydmljZSkscyg2LGYuSU9wdGlvbnNTZXJ2aWNlKSxzKDcsaC5JUmVuZGVyU2VydmljZSldLHQpfShwLkRpc3Bvc2FibGUpO3QuU2VsZWN0aW9uU2VydmljZT1tfSw0NzI1OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5JQ2hhcmFjdGVySm9pbmVyU2VydmljZT10LklTb3VuZFNlcnZpY2U9dC5JU2VsZWN0aW9uU2VydmljZT10LklSZW5kZXJTZXJ2aWNlPXQuSU1vdXNlU2VydmljZT10LklDb3JlQnJvd3NlclNlcnZpY2U9dC5JQ2hhclNpemVTZXJ2aWNlPXZvaWQgMDt2YXIgaT1yKDgzNDMpO3QuSUNoYXJTaXplU2VydmljZT0oMCxpLmNyZWF0ZURlY29yYXRvcikoIkNoYXJTaXplU2VydmljZSIpLHQuSUNvcmVCcm93c2VyU2VydmljZT0oMCxpLmNyZWF0ZURlY29yYXRvcikoIkNvcmVCcm93c2VyU2VydmljZSIpLHQuSU1vdXNlU2VydmljZT0oMCxpLmNyZWF0ZURlY29yYXRvcikoIk1vdXNlU2VydmljZSIpLHQuSVJlbmRlclNlcnZpY2U9KDAsaS5jcmVhdGVEZWNvcmF0b3IpKCJSZW5kZXJTZXJ2aWNlIiksdC5JU2VsZWN0aW9uU2VydmljZT0oMCxpLmNyZWF0ZURlY29yYXRvcikoIlNlbGVjdGlvblNlcnZpY2UiKSx0LklTb3VuZFNlcnZpY2U9KDAsaS5jcmVhdGVEZWNvcmF0b3IpKCJTb3VuZFNlcnZpY2UiKSx0LklDaGFyYWN0ZXJKb2luZXJTZXJ2aWNlPSgwLGkuY3JlYXRlRGVjb3JhdG9yKSgiQ2hhcmFjdGVySm9pbmVyU2VydmljZSIpfSwzNTc6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Tb3VuZFNlcnZpY2U9dm9pZCAwO3ZhciBvPXIoMjU4NSkscz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fb3B0aW9uc1NlcnZpY2U9ZX1yZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGUsImF1ZGlvQ29udGV4dCIse2dldDpmdW5jdGlvbigpe2lmKCFlLl9hdWRpb0NvbnRleHQpe3ZhciB0PXdpbmRvdy5BdWRpb0NvbnRleHR8fHdpbmRvdy53ZWJraXRBdWRpb0NvbnRleHQ7aWYoIXQpcmV0dXJuIGNvbnNvbGUud2FybigiV2ViIEF1ZGlvIEFQSSBpcyBub3Qgc3VwcG9ydGVkIGJ5IHRoaXMgYnJvd3Nlci4gQ29uc2lkZXIgdXBncmFkaW5nIHRvIHRoZSBsYXRlc3QgdmVyc2lvbiIpLG51bGw7ZS5fYXVkaW9Db250ZXh0PW5ldyB0fXJldHVybiBlLl9hdWRpb0NvbnRleHR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUucGxheUJlbGxTb3VuZD1mdW5jdGlvbigpe3ZhciB0PWUuYXVkaW9Db250ZXh0O2lmKHQpe3ZhciByPXQuY3JlYXRlQnVmZmVyU291cmNlKCk7dC5kZWNvZGVBdWRpb0RhdGEodGhpcy5fYmFzZTY0VG9BcnJheUJ1ZmZlcih0aGlzLl9yZW1vdmVNaW1lVHlwZSh0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmJlbGxTb3VuZCkpLChmdW5jdGlvbihlKXtyLmJ1ZmZlcj1lLHIuY29ubmVjdCh0LmRlc3RpbmF0aW9uKSxyLnN0YXJ0KDApfSkpfX0sZS5wcm90b3R5cGUuX2Jhc2U2NFRvQXJyYXlCdWZmZXI9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PXdpbmRvdy5hdG9iKGUpLHI9dC5sZW5ndGgsaT1uZXcgVWludDhBcnJheShyKSxuPTA7bjxyO24rKylpW25dPXQuY2hhckNvZGVBdChuKTtyZXR1cm4gaS5idWZmZXJ9LGUucHJvdG90eXBlLl9yZW1vdmVNaW1lVHlwZT1mdW5jdGlvbihlKXtyZXR1cm4gZS5zcGxpdCgiLCIpWzFdfSxlPWkoW24oMCxvLklPcHRpb25zU2VydmljZSldLGUpfSgpO3QuU291bmRTZXJ2aWNlPXN9LDYzNDk6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkNpcmN1bGFyTGlzdD12b2lkIDA7dmFyIGk9cig4NDYwKSxuPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9tYXhMZW5ndGg9ZSx0aGlzLm9uRGVsZXRlRW1pdHRlcj1uZXcgaS5FdmVudEVtaXR0ZXIsdGhpcy5vbkluc2VydEVtaXR0ZXI9bmV3IGkuRXZlbnRFbWl0dGVyLHRoaXMub25UcmltRW1pdHRlcj1uZXcgaS5FdmVudEVtaXR0ZXIsdGhpcy5fYXJyYXk9bmV3IEFycmF5KHRoaXMuX21heExlbmd0aCksdGhpcy5fc3RhcnRJbmRleD0wLHRoaXMuX2xlbmd0aD0wfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uRGVsZXRlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMub25EZWxldGVFbWl0dGVyLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25JbnNlcnQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5vbkluc2VydEVtaXR0ZXIuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvblRyaW0iLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5vblRyaW1FbWl0dGVyLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwibWF4TGVuZ3RoIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX21heExlbmd0aH0sc2V0OmZ1bmN0aW9uKGUpe2lmKHRoaXMuX21heExlbmd0aCE9PWUpe2Zvcih2YXIgdD1uZXcgQXJyYXkoZSkscj0wO3I8TWF0aC5taW4oZSx0aGlzLmxlbmd0aCk7cisrKXRbcl09dGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgocildO3RoaXMuX2FycmF5PXQsdGhpcy5fbWF4TGVuZ3RoPWUsdGhpcy5fc3RhcnRJbmRleD0wfX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImxlbmd0aCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9sZW5ndGh9LHNldDpmdW5jdGlvbihlKXtpZihlPnRoaXMuX2xlbmd0aClmb3IodmFyIHQ9dGhpcy5fbGVuZ3RoO3Q8ZTt0KyspdGhpcy5fYXJyYXlbdF09dm9pZCAwO3RoaXMuX2xlbmd0aD1lfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgoZSldfSxlLnByb3RvdHlwZS5zZXQ9ZnVuY3Rpb24oZSx0KXt0aGlzLl9hcnJheVt0aGlzLl9nZXRDeWNsaWNJbmRleChlKV09dH0sZS5wcm90b3R5cGUucHVzaD1mdW5jdGlvbihlKXt0aGlzLl9hcnJheVt0aGlzLl9nZXRDeWNsaWNJbmRleCh0aGlzLl9sZW5ndGgpXT1lLHRoaXMuX2xlbmd0aD09PXRoaXMuX21heExlbmd0aD8odGhpcy5fc3RhcnRJbmRleD0rK3RoaXMuX3N0YXJ0SW5kZXgldGhpcy5fbWF4TGVuZ3RoLHRoaXMub25UcmltRW1pdHRlci5maXJlKDEpKTp0aGlzLl9sZW5ndGgrK30sZS5wcm90b3R5cGUucmVjeWNsZT1mdW5jdGlvbigpe2lmKHRoaXMuX2xlbmd0aCE9PXRoaXMuX21heExlbmd0aCl0aHJvdyBuZXcgRXJyb3IoIkNhbiBvbmx5IHJlY3ljbGUgd2hlbiB0aGUgYnVmZmVyIGlzIGZ1bGwiKTtyZXR1cm4gdGhpcy5fc3RhcnRJbmRleD0rK3RoaXMuX3N0YXJ0SW5kZXgldGhpcy5fbWF4TGVuZ3RoLHRoaXMub25UcmltRW1pdHRlci5maXJlKDEpLHRoaXMuX2FycmF5W3RoaXMuX2dldEN5Y2xpY0luZGV4KHRoaXMuX2xlbmd0aC0xKV19LE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiaXNGdWxsIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2xlbmd0aD09PXRoaXMuX21heExlbmd0aH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5wb3A9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgodGhpcy5fbGVuZ3RoLS0tMSldfSxlLnByb3RvdHlwZS5zcGxpY2U9ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHI9W10saT0yO2k8YXJndW1lbnRzLmxlbmd0aDtpKyspcltpLTJdPWFyZ3VtZW50c1tpXTtpZih0KXtmb3IodmFyIG49ZTtuPHRoaXMuX2xlbmd0aC10O24rKyl0aGlzLl9hcnJheVt0aGlzLl9nZXRDeWNsaWNJbmRleChuKV09dGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgobit0KV07dGhpcy5fbGVuZ3RoLT10LHRoaXMub25EZWxldGVFbWl0dGVyLmZpcmUoe2luZGV4OmUsYW1vdW50OnR9KX1mb3Iobj10aGlzLl9sZW5ndGgtMTtuPj1lO24tLSl0aGlzLl9hcnJheVt0aGlzLl9nZXRDeWNsaWNJbmRleChuK3IubGVuZ3RoKV09dGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgobildO2ZvcihuPTA7bjxyLmxlbmd0aDtuKyspdGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgoZStuKV09cltuXTtpZihyLmxlbmd0aCYmdGhpcy5vbkluc2VydEVtaXR0ZXIuZmlyZSh7aW5kZXg6ZSxhbW91bnQ6ci5sZW5ndGh9KSx0aGlzLl9sZW5ndGgrci5sZW5ndGg+dGhpcy5fbWF4TGVuZ3RoKXt2YXIgbz10aGlzLl9sZW5ndGgrci5sZW5ndGgtdGhpcy5fbWF4TGVuZ3RoO3RoaXMuX3N0YXJ0SW5kZXgrPW8sdGhpcy5fbGVuZ3RoPXRoaXMuX21heExlbmd0aCx0aGlzLm9uVHJpbUVtaXR0ZXIuZmlyZShvKX1lbHNlIHRoaXMuX2xlbmd0aCs9ci5sZW5ndGh9LGUucHJvdG90eXBlLnRyaW1TdGFydD1mdW5jdGlvbihlKXtlPnRoaXMuX2xlbmd0aCYmKGU9dGhpcy5fbGVuZ3RoKSx0aGlzLl9zdGFydEluZGV4Kz1lLHRoaXMuX2xlbmd0aC09ZSx0aGlzLm9uVHJpbUVtaXR0ZXIuZmlyZShlKX0sZS5wcm90b3R5cGUuc2hpZnRFbGVtZW50cz1mdW5jdGlvbihlLHQscil7aWYoISh0PD0wKSl7aWYoZTwwfHxlPj10aGlzLl9sZW5ndGgpdGhyb3cgbmV3IEVycm9yKCJzdGFydCBhcmd1bWVudCBvdXQgb2YgcmFuZ2UiKTtpZihlK3I8MCl0aHJvdyBuZXcgRXJyb3IoIkNhbm5vdCBzaGlmdCBlbGVtZW50cyBpbiBsaXN0IGJleW9uZCBpbmRleCAwIik7aWYocj4wKXtmb3IodmFyIGk9dC0xO2k+PTA7aS0tKXRoaXMuc2V0KGUraStyLHRoaXMuZ2V0KGUraSkpO3ZhciBuPWUrdCtyLXRoaXMuX2xlbmd0aDtpZihuPjApZm9yKHRoaXMuX2xlbmd0aCs9bjt0aGlzLl9sZW5ndGg+dGhpcy5fbWF4TGVuZ3RoOyl0aGlzLl9sZW5ndGgtLSx0aGlzLl9zdGFydEluZGV4KyssdGhpcy5vblRyaW1FbWl0dGVyLmZpcmUoMSl9ZWxzZSBmb3IoaT0wO2k8dDtpKyspdGhpcy5zZXQoZStpK3IsdGhpcy5nZXQoZStpKSl9fSxlLnByb3RvdHlwZS5fZ2V0Q3ljbGljSW5kZXg9ZnVuY3Rpb24oZSl7cmV0dXJuKHRoaXMuX3N0YXJ0SW5kZXgrZSkldGhpcy5fbWF4TGVuZ3RofSxlfSgpO3QuQ2lyY3VsYXJMaXN0PW59LDE0Mzk6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5jbG9uZT12b2lkIDAsdC5jbG9uZT1mdW5jdGlvbiBlKHQscil7aWYodm9pZCAwPT09ciYmKHI9NSksIm9iamVjdCIhPXR5cGVvZiB0KXJldHVybiB0O3ZhciBpPUFycmF5LmlzQXJyYXkodCk/W106e307Zm9yKHZhciBuIGluIHQpaVtuXT1yPD0xP3Rbbl06dFtuXSYmZSh0W25dLHItMSk7cmV0dXJuIGl9fSw4OTY5OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkNvcmVUZXJtaW5hbD12b2lkIDA7dmFyIG89cig4NDQpLHM9cigyNTg1KSxhPXIoNDM0OCksYz1yKDc4NjYpLGw9cig3NDQpLHU9cig3MzAyKSxoPXIoNjk3NSksZj1yKDg0NjApLF89cigxNzUzKSxkPXIoMzczMCkscD1yKDE0ODApLHY9cig3OTk0KSxnPXIoOTI4MikseT1yKDU0MzUpLG09cig1OTgxKSxiPSExLFM9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0KXt2YXIgcj1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIHIuX29uQmluYXJ5PW5ldyBmLkV2ZW50RW1pdHRlcixyLl9vbkRhdGE9bmV3IGYuRXZlbnRFbWl0dGVyLHIuX29uTGluZUZlZWQ9bmV3IGYuRXZlbnRFbWl0dGVyLHIuX29uUmVzaXplPW5ldyBmLkV2ZW50RW1pdHRlcixyLl9vblNjcm9sbD1uZXcgZi5FdmVudEVtaXR0ZXIsci5faW5zdGFudGlhdGlvblNlcnZpY2U9bmV3IGEuSW5zdGFudGlhdGlvblNlcnZpY2Usci5vcHRpb25zU2VydmljZT1uZXcgdS5PcHRpb25zU2VydmljZSh0KSxyLl9pbnN0YW50aWF0aW9uU2VydmljZS5zZXRTZXJ2aWNlKHMuSU9wdGlvbnNTZXJ2aWNlLHIub3B0aW9uc1NlcnZpY2UpLHIuX2J1ZmZlclNlcnZpY2U9ci5yZWdpc3RlcihyLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShsLkJ1ZmZlclNlcnZpY2UpKSxyLl9pbnN0YW50aWF0aW9uU2VydmljZS5zZXRTZXJ2aWNlKHMuSUJ1ZmZlclNlcnZpY2Usci5fYnVmZmVyU2VydmljZSksci5fbG9nU2VydmljZT1yLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShjLkxvZ1NlcnZpY2UpLHIuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2Uocy5JTG9nU2VydmljZSxyLl9sb2dTZXJ2aWNlKSxyLmNvcmVTZXJ2aWNlPXIucmVnaXN0ZXIoci5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2UoaC5Db3JlU2VydmljZSwoZnVuY3Rpb24oKXtyZXR1cm4gci5zY3JvbGxUb0JvdHRvbSgpfSkpKSxyLl9pbnN0YW50aWF0aW9uU2VydmljZS5zZXRTZXJ2aWNlKHMuSUNvcmVTZXJ2aWNlLHIuY29yZVNlcnZpY2UpLHIuY29yZU1vdXNlU2VydmljZT1yLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShfLkNvcmVNb3VzZVNlcnZpY2UpLHIuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2Uocy5JQ29yZU1vdXNlU2VydmljZSxyLmNvcmVNb3VzZVNlcnZpY2UpLHIuX2RpcnR5Um93U2VydmljZT1yLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShkLkRpcnR5Um93U2VydmljZSksci5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShzLklEaXJ0eVJvd1NlcnZpY2Usci5fZGlydHlSb3dTZXJ2aWNlKSxyLnVuaWNvZGVTZXJ2aWNlPXIuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKHAuVW5pY29kZVNlcnZpY2UpLHIuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2Uocy5JVW5pY29kZVNlcnZpY2Usci51bmljb2RlU2VydmljZSksci5fY2hhcnNldFNlcnZpY2U9ci5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2Uodi5DaGFyc2V0U2VydmljZSksci5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShzLklDaGFyc2V0U2VydmljZSxyLl9jaGFyc2V0U2VydmljZSksci5faW5wdXRIYW5kbGVyPW5ldyB5LklucHV0SGFuZGxlcihyLl9idWZmZXJTZXJ2aWNlLHIuX2NoYXJzZXRTZXJ2aWNlLHIuY29yZVNlcnZpY2Usci5fZGlydHlSb3dTZXJ2aWNlLHIuX2xvZ1NlcnZpY2Usci5vcHRpb25zU2VydmljZSxyLmNvcmVNb3VzZVNlcnZpY2Usci51bmljb2RlU2VydmljZSksci5yZWdpc3RlcigoMCxmLmZvcndhcmRFdmVudCkoci5faW5wdXRIYW5kbGVyLm9uTGluZUZlZWQsci5fb25MaW5lRmVlZCkpLHIucmVnaXN0ZXIoci5faW5wdXRIYW5kbGVyKSxyLnJlZ2lzdGVyKCgwLGYuZm9yd2FyZEV2ZW50KShyLl9idWZmZXJTZXJ2aWNlLm9uUmVzaXplLHIuX29uUmVzaXplKSksci5yZWdpc3RlcigoMCxmLmZvcndhcmRFdmVudCkoci5jb3JlU2VydmljZS5vbkRhdGEsci5fb25EYXRhKSksci5yZWdpc3RlcigoMCxmLmZvcndhcmRFdmVudCkoci5jb3JlU2VydmljZS5vbkJpbmFyeSxyLl9vbkJpbmFyeSkpLHIucmVnaXN0ZXIoci5vcHRpb25zU2VydmljZS5vbk9wdGlvbkNoYW5nZSgoZnVuY3Rpb24oZSl7cmV0dXJuIHIuX3VwZGF0ZU9wdGlvbnMoZSl9KSkpLHIucmVnaXN0ZXIoci5fYnVmZmVyU2VydmljZS5vblNjcm9sbCgoZnVuY3Rpb24oZSl7ci5fb25TY3JvbGwuZmlyZSh7cG9zaXRpb246ci5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3Asc291cmNlOjB9KSxyLl9kaXJ0eVJvd1NlcnZpY2UubWFya1JhbmdlRGlydHkoci5fYnVmZmVyU2VydmljZS5idWZmZXIuc2Nyb2xsVG9wLHIuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnNjcm9sbEJvdHRvbSl9KSkpLHIucmVnaXN0ZXIoci5faW5wdXRIYW5kbGVyLm9uU2Nyb2xsKChmdW5jdGlvbihlKXtyLl9vblNjcm9sbC5maXJlKHtwb3NpdGlvbjpyLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCxzb3VyY2U6MH0pLHIuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eShyLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci5zY3JvbGxUb3Asci5fYnVmZmVyU2VydmljZS5idWZmZXIuc2Nyb2xsQm90dG9tKX0pKSksci5fd3JpdGVCdWZmZXI9bmV3IG0uV3JpdGVCdWZmZXIoKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIHIuX2lucHV0SGFuZGxlci5wYXJzZShlLHQpfSkpLHJ9cmV0dXJuIG4odCxlKSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQmluYXJ5Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uQmluYXJ5LmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25EYXRhIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uRGF0YS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uTGluZUZlZWQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25MaW5lRmVlZC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uUmVzaXplIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uUmVzaXplLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25TY3JvbGwiLHtnZXQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3JldHVybiB0aGlzLl9vblNjcm9sbEFwaXx8KHRoaXMuX29uU2Nyb2xsQXBpPW5ldyBmLkV2ZW50RW1pdHRlcix0aGlzLnJlZ2lzdGVyKHRoaXMuX29uU2Nyb2xsLmV2ZW50KChmdW5jdGlvbih0KXt2YXIgcjtudWxsPT09KHI9ZS5fb25TY3JvbGxBcGkpfHx2b2lkIDA9PT1yfHxyLmZpcmUodC5wb3NpdGlvbil9KSkpKSx0aGlzLl9vblNjcm9sbEFwaS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsImNvbHMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwicm93cyIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3N9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJidWZmZXJzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyc30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9wdGlvbnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zfSxzZXQ6ZnVuY3Rpb24oZSl7Zm9yKHZhciB0IGluIGUpdGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zW3RdPWVbdF19LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3ZhciB0O3RoaXMuX2lzRGlzcG9zZWR8fChlLnByb3RvdHlwZS5kaXNwb3NlLmNhbGwodGhpcyksbnVsbD09PSh0PXRoaXMuX3dpbmRvd3NNb2RlKXx8dm9pZCAwPT09dHx8dC5kaXNwb3NlKCksdGhpcy5fd2luZG93c01vZGU9dm9pZCAwKX0sdC5wcm90b3R5cGUud3JpdGU9ZnVuY3Rpb24oZSx0KXt0aGlzLl93cml0ZUJ1ZmZlci53cml0ZShlLHQpfSx0LnByb3RvdHlwZS53cml0ZVN5bmM9ZnVuY3Rpb24oZSx0KXt0aGlzLl9sb2dTZXJ2aWNlLmxvZ0xldmVsPD1zLkxvZ0xldmVsRW51bS5XQVJOJiYhYiYmKHRoaXMuX2xvZ1NlcnZpY2Uud2Fybigid3JpdGVTeW5jIGlzIHVucmVsaWFibGUgYW5kIHdpbGwgYmUgcmVtb3ZlZCBzb29uLiIpLGI9ITApLHRoaXMuX3dyaXRlQnVmZmVyLndyaXRlU3luYyhlLHQpfSx0LnByb3RvdHlwZS5yZXNpemU9ZnVuY3Rpb24oZSx0KXtpc05hTihlKXx8aXNOYU4odCl8fChlPU1hdGgubWF4KGUsbC5NSU5JTVVNX0NPTFMpLHQ9TWF0aC5tYXgodCxsLk1JTklNVU1fUk9XUyksdGhpcy5fYnVmZmVyU2VydmljZS5yZXNpemUoZSx0KSl9LHQucHJvdG90eXBlLnNjcm9sbD1mdW5jdGlvbihlLHQpe3ZvaWQgMD09PXQmJih0PSExKSx0aGlzLl9idWZmZXJTZXJ2aWNlLnNjcm9sbChlLHQpfSx0LnByb3RvdHlwZS5zY3JvbGxMaW5lcz1mdW5jdGlvbihlLHQscil7dGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGxMaW5lcyhlLHQscil9LHQucHJvdG90eXBlLnNjcm9sbFBhZ2VzPWZ1bmN0aW9uKGUpe3RoaXMuX2J1ZmZlclNlcnZpY2Uuc2Nyb2xsUGFnZXMoZSl9LHQucHJvdG90eXBlLnNjcm9sbFRvVG9wPWZ1bmN0aW9uKCl7dGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGxUb1RvcCgpfSx0LnByb3RvdHlwZS5zY3JvbGxUb0JvdHRvbT1mdW5jdGlvbigpe3RoaXMuX2J1ZmZlclNlcnZpY2Uuc2Nyb2xsVG9Cb3R0b20oKX0sdC5wcm90b3R5cGUuc2Nyb2xsVG9MaW5lPWZ1bmN0aW9uKGUpe3RoaXMuX2J1ZmZlclNlcnZpY2Uuc2Nyb2xsVG9MaW5lKGUpfSx0LnByb3RvdHlwZS5yZWdpc3RlckVzY0hhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5faW5wdXRIYW5kbGVyLnJlZ2lzdGVyRXNjSGFuZGxlcihlLHQpfSx0LnByb3RvdHlwZS5yZWdpc3RlckRjc0hhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5faW5wdXRIYW5kbGVyLnJlZ2lzdGVyRGNzSGFuZGxlcihlLHQpfSx0LnByb3RvdHlwZS5yZWdpc3RlckNzaUhhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5faW5wdXRIYW5kbGVyLnJlZ2lzdGVyQ3NpSGFuZGxlcihlLHQpfSx0LnByb3RvdHlwZS5yZWdpc3Rlck9zY0hhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5faW5wdXRIYW5kbGVyLnJlZ2lzdGVyT3NjSGFuZGxlcihlLHQpfSx0LnByb3RvdHlwZS5fc2V0dXA9ZnVuY3Rpb24oKXt0aGlzLm9wdGlvbnNTZXJ2aWNlLm9wdGlvbnMud2luZG93c01vZGUmJnRoaXMuX2VuYWJsZVdpbmRvd3NNb2RlKCl9LHQucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5faW5wdXRIYW5kbGVyLnJlc2V0KCksdGhpcy5fYnVmZmVyU2VydmljZS5yZXNldCgpLHRoaXMuX2NoYXJzZXRTZXJ2aWNlLnJlc2V0KCksdGhpcy5jb3JlU2VydmljZS5yZXNldCgpLHRoaXMuY29yZU1vdXNlU2VydmljZS5yZXNldCgpfSx0LnByb3RvdHlwZS5fdXBkYXRlT3B0aW9ucz1mdW5jdGlvbihlKXt2YXIgdDtzd2l0Y2goZSl7Y2FzZSJzY3JvbGxiYWNrIjp0aGlzLmJ1ZmZlcnMucmVzaXplKHRoaXMuY29scyx0aGlzLnJvd3MpO2JyZWFrO2Nhc2Uid2luZG93c01vZGUiOnRoaXMub3B0aW9uc1NlcnZpY2Uub3B0aW9ucy53aW5kb3dzTW9kZT90aGlzLl9lbmFibGVXaW5kb3dzTW9kZSgpOihudWxsPT09KHQ9dGhpcy5fd2luZG93c01vZGUpfHx2b2lkIDA9PT10fHx0LmRpc3Bvc2UoKSx0aGlzLl93aW5kb3dzTW9kZT12b2lkIDApfX0sdC5wcm90b3R5cGUuX2VuYWJsZVdpbmRvd3NNb2RlPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcztpZighdGhpcy5fd2luZG93c01vZGUpe3ZhciB0PVtdO3QucHVzaCh0aGlzLm9uTGluZUZlZWQoZy51cGRhdGVXaW5kb3dzTW9kZVdyYXBwZWRTdGF0ZS5iaW5kKG51bGwsdGhpcy5fYnVmZmVyU2VydmljZSkpKSx0LnB1c2godGhpcy5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJIIn0sKGZ1bmN0aW9uKCl7cmV0dXJuKDAsZy51cGRhdGVXaW5kb3dzTW9kZVdyYXBwZWRTdGF0ZSkoZS5fYnVmZmVyU2VydmljZSksITF9KSkpLHRoaXMuX3dpbmRvd3NNb2RlPXtkaXNwb3NlOmZ1bmN0aW9uKCl7Zm9yKHZhciBlPTAscj10O2U8ci5sZW5ndGg7ZSsrKXJbZV0uZGlzcG9zZSgpfX19fSx0fShvLkRpc3Bvc2FibGUpO3QuQ29yZVRlcm1pbmFsPVN9LDg0NjA6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5mb3J3YXJkRXZlbnQ9dC5FdmVudEVtaXR0ZXI9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX2xpc3RlbmVycz1bXSx0aGlzLl9kaXNwb3NlZD0hMX1yZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJldmVudCIse2dldDpmdW5jdGlvbigpe3ZhciBlPXRoaXM7cmV0dXJuIHRoaXMuX2V2ZW50fHwodGhpcy5fZXZlbnQ9ZnVuY3Rpb24odCl7cmV0dXJuIGUuX2xpc3RlbmVycy5wdXNoKHQpLHtkaXNwb3NlOmZ1bmN0aW9uKCl7aWYoIWUuX2Rpc3Bvc2VkKWZvcih2YXIgcj0wO3I8ZS5fbGlzdGVuZXJzLmxlbmd0aDtyKyspaWYoZS5fbGlzdGVuZXJzW3JdPT09dClyZXR1cm4gdm9pZCBlLl9saXN0ZW5lcnMuc3BsaWNlKHIsMSl9fX0pLHRoaXMuX2V2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmZpcmU9ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHI9W10saT0wO2k8dGhpcy5fbGlzdGVuZXJzLmxlbmd0aDtpKyspci5wdXNoKHRoaXMuX2xpc3RlbmVyc1tpXSk7Zm9yKGk9MDtpPHIubGVuZ3RoO2krKylyW2ldLmNhbGwodm9pZCAwLGUsdCl9LGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt0aGlzLl9saXN0ZW5lcnMmJih0aGlzLl9saXN0ZW5lcnMubGVuZ3RoPTApLHRoaXMuX2Rpc3Bvc2VkPSEwfSxlfSgpO3QuRXZlbnRFbWl0dGVyPXIsdC5mb3J3YXJkRXZlbnQ9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSgoZnVuY3Rpb24oZSl7cmV0dXJuIHQuZmlyZShlKX0pKX19LDU0MzU6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuSW5wdXRIYW5kbGVyPXQuV2luZG93c09wdGlvbnNSZXBvcnRUeXBlPXZvaWQgMDt2YXIgbyxzPXIoMjU4NCksYT1yKDcxMTYpLGM9cigyMDE1KSxsPXIoODQ0KSx1PXIoODI3MyksaD1yKDQ4MiksZj1yKDg0MzcpLF89cig4NDYwKSxkPXIoNjQzKSxwPXIoNTExKSx2PXIoMzczNCksZz1yKDI1ODUpLHk9cig2MjQyKSxtPXIoNjM1MSksYj1yKDU5NDEpLFM9eyIoIjowLCIpIjoxLCIqIjoyLCIrIjozLCItIjoxLCIuIjoyfSxDPTEzMTA3MjtmdW5jdGlvbiB3KGUsdCl7aWYoZT4yNClyZXR1cm4gdC5zZXRXaW5MaW5lc3x8ITE7c3dpdGNoKGUpe2Nhc2UgMTpyZXR1cm4hIXQucmVzdG9yZVdpbjtjYXNlIDI6cmV0dXJuISF0Lm1pbmltaXplV2luO2Nhc2UgMzpyZXR1cm4hIXQuc2V0V2luUG9zaXRpb247Y2FzZSA0OnJldHVybiEhdC5zZXRXaW5TaXplUGl4ZWxzO2Nhc2UgNTpyZXR1cm4hIXQucmFpc2VXaW47Y2FzZSA2OnJldHVybiEhdC5sb3dlcldpbjtjYXNlIDc6cmV0dXJuISF0LnJlZnJlc2hXaW47Y2FzZSA4OnJldHVybiEhdC5zZXRXaW5TaXplQ2hhcnM7Y2FzZSA5OnJldHVybiEhdC5tYXhpbWl6ZVdpbjtjYXNlIDEwOnJldHVybiEhdC5mdWxsc2NyZWVuV2luO2Nhc2UgMTE6cmV0dXJuISF0LmdldFdpblN0YXRlO2Nhc2UgMTM6cmV0dXJuISF0LmdldFdpblBvc2l0aW9uO2Nhc2UgMTQ6cmV0dXJuISF0LmdldFdpblNpemVQaXhlbHM7Y2FzZSAxNTpyZXR1cm4hIXQuZ2V0U2NyZWVuU2l6ZVBpeGVscztjYXNlIDE2OnJldHVybiEhdC5nZXRDZWxsU2l6ZVBpeGVscztjYXNlIDE4OnJldHVybiEhdC5nZXRXaW5TaXplQ2hhcnM7Y2FzZSAxOTpyZXR1cm4hIXQuZ2V0U2NyZWVuU2l6ZUNoYXJzO2Nhc2UgMjA6cmV0dXJuISF0LmdldEljb25UaXRsZTtjYXNlIDIxOnJldHVybiEhdC5nZXRXaW5UaXRsZTtjYXNlIDIyOnJldHVybiEhdC5wdXNoVGl0bGU7Y2FzZSAyMzpyZXR1cm4hIXQucG9wVGl0bGU7Y2FzZSAyNDpyZXR1cm4hIXQuc2V0V2luTGluZXN9cmV0dXJuITF9IWZ1bmN0aW9uKGUpe2VbZS5HRVRfV0lOX1NJWkVfUElYRUxTPTBdPSJHRVRfV0lOX1NJWkVfUElYRUxTIixlW2UuR0VUX0NFTExfU0laRV9QSVhFTFM9MV09IkdFVF9DRUxMX1NJWkVfUElYRUxTIn0obz10LldpbmRvd3NPcHRpb25zUmVwb3J0VHlwZXx8KHQuV2luZG93c09wdGlvbnNSZXBvcnRUeXBlPXt9KSk7dmFyIEw9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyLGkpe3RoaXMuX2J1ZmZlclNlcnZpY2U9ZSx0aGlzLl9jb3JlU2VydmljZT10LHRoaXMuX2xvZ1NlcnZpY2U9cix0aGlzLl9vcHRpb25zU2VydmljZT1pLHRoaXMuX2RhdGE9bmV3IFVpbnQzMkFycmF5KDApfXJldHVybiBlLnByb3RvdHlwZS5ob29rPWZ1bmN0aW9uKGUpe3RoaXMuX2RhdGE9bmV3IFVpbnQzMkFycmF5KDApfSxlLnByb3RvdHlwZS5wdXQ9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2RhdGE9KDAsdS5jb25jYXQpKHRoaXMuX2RhdGEsZS5zdWJhcnJheSh0LHIpKX0sZS5wcm90b3R5cGUudW5ob29rPWZ1bmN0aW9uKGUpe2lmKCFlKXJldHVybiB0aGlzLl9kYXRhPW5ldyBVaW50MzJBcnJheSgwKSwhMDt2YXIgdD0oMCxoLnV0ZjMyVG9TdHJpbmcpKHRoaXMuX2RhdGEpO3N3aXRjaCh0aGlzLl9kYXRhPW5ldyBVaW50MzJBcnJheSgwKSx0KXtjYXNlJyJxJzp0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKydQMSRyMCJxJytzLkMwLkVTQysiXFwiKTticmVhaztjYXNlJyJwJzp0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKydQMSRyNjE7MSJwJytzLkMwLkVTQysiXFwiKTticmVhaztjYXNlInIiOnZhciByPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnNjcm9sbFRvcCsxKyI7IisodGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIuc2Nyb2xsQm90dG9tKzEpKyJyIjt0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKyJQMSRyIityK3MuQzAuRVNDKyJcXCIpO2JyZWFrO2Nhc2UibSI6dGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChzLkMwLkVTQysiUDEkcjBtIitzLkMwLkVTQysiXFwiKTticmVhaztjYXNlIiBxIjp2YXIgaT17YmxvY2s6Mix1bmRlcmxpbmU6NCxiYXI6Nn1bdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JTdHlsZV07aS09dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JCbGluaz8xOjAsdGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChzLkMwLkVTQysiUDEkciIraSsiIHEiK3MuQzAuRVNDKyJcXCIpO2JyZWFrO2RlZmF1bHQ6dGhpcy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBEQ1MgJHEgJXMiLHQpLHRoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIlAwJHIiK3MuQzAuRVNDKyJcXCIpfXJldHVybiEwfSxlfSgpLEU9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8sbCx1LGQsdil7dm9pZCAwPT09diYmKHY9bmV3IGMuRXNjYXBlU2VxdWVuY2VQYXJzZXIpO3ZhciBnPWUuY2FsbCh0aGlzKXx8dGhpcztnLl9idWZmZXJTZXJ2aWNlPXQsZy5fY2hhcnNldFNlcnZpY2U9cixnLl9jb3JlU2VydmljZT1pLGcuX2RpcnR5Um93U2VydmljZT1uLGcuX2xvZ1NlcnZpY2U9byxnLl9vcHRpb25zU2VydmljZT1sLGcuX2NvcmVNb3VzZVNlcnZpY2U9dSxnLl91bmljb2RlU2VydmljZT1kLGcuX3BhcnNlcj12LGcuX3BhcnNlQnVmZmVyPW5ldyBVaW50MzJBcnJheSg0MDk2KSxnLl9zdHJpbmdEZWNvZGVyPW5ldyBoLlN0cmluZ1RvVXRmMzIsZy5fdXRmOERlY29kZXI9bmV3IGguVXRmOFRvVXRmMzIsZy5fd29ya0NlbGw9bmV3IHAuQ2VsbERhdGEsZy5fd2luZG93VGl0bGU9IiIsZy5faWNvbk5hbWU9IiIsZy5fd2luZG93VGl0bGVTdGFjaz1bXSxnLl9pY29uTmFtZVN0YWNrPVtdLGcuX2N1ckF0dHJEYXRhPWYuREVGQVVMVF9BVFRSX0RBVEEuY2xvbmUoKSxnLl9lcmFzZUF0dHJEYXRhSW50ZXJuYWw9Zi5ERUZBVUxUX0FUVFJfREFUQS5jbG9uZSgpLGcuX29uUmVxdWVzdEJlbGw9bmV3IF8uRXZlbnRFbWl0dGVyLGcuX29uUmVxdWVzdFJlZnJlc2hSb3dzPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vblJlcXVlc3RSZXNldD1uZXcgXy5FdmVudEVtaXR0ZXIsZy5fb25SZXF1ZXN0U2VuZEZvY3VzPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vblJlcXVlc3RTeW5jU2Nyb2xsQmFyPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vblJlcXVlc3RXaW5kb3dzT3B0aW9uc1JlcG9ydD1uZXcgXy5FdmVudEVtaXR0ZXIsZy5fb25BMTF5Q2hhcj1uZXcgXy5FdmVudEVtaXR0ZXIsZy5fb25BMTF5VGFiPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vbkN1cnNvck1vdmU9bmV3IF8uRXZlbnRFbWl0dGVyLGcuX29uTGluZUZlZWQ9bmV3IF8uRXZlbnRFbWl0dGVyLGcuX29uU2Nyb2xsPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vblRpdGxlQ2hhbmdlPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vbkNvbG9yPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9wYXJzZVN0YWNrPXtwYXVzZWQ6ITEsY3Vyc29yU3RhcnRYOjAsY3Vyc29yU3RhcnRZOjAsZGVjb2RlZExlbmd0aDowLHBvc2l0aW9uOjB9LGcuX3NwZWNpYWxDb2xvcnM9WzI1NiwyNTcsMjU4XSxnLnJlZ2lzdGVyKGcuX3BhcnNlciksZy5fYWN0aXZlQnVmZmVyPWcuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLGcucmVnaXN0ZXIoZy5fYnVmZmVyU2VydmljZS5idWZmZXJzLm9uQnVmZmVyQWN0aXZhdGUoKGZ1bmN0aW9uKGUpe3JldHVybiBnLl9hY3RpdmVCdWZmZXI9ZS5hY3RpdmVCdWZmZXJ9KSkpLGcuX3BhcnNlci5zZXRDc2lIYW5kbGVyRmFsbGJhY2soKGZ1bmN0aW9uKGUsdCl7Zy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBDU0kgY29kZTogIix7aWRlbnRpZmllcjpnLl9wYXJzZXIuaWRlbnRUb1N0cmluZyhlKSxwYXJhbXM6dC50b0FycmF5KCl9KX0pKSxnLl9wYXJzZXIuc2V0RXNjSGFuZGxlckZhbGxiYWNrKChmdW5jdGlvbihlKXtnLl9sb2dTZXJ2aWNlLmRlYnVnKCJVbmtub3duIEVTQyBjb2RlOiAiLHtpZGVudGlmaWVyOmcuX3BhcnNlci5pZGVudFRvU3RyaW5nKGUpfSl9KSksZy5fcGFyc2VyLnNldEV4ZWN1dGVIYW5kbGVyRmFsbGJhY2soKGZ1bmN0aW9uKGUpe2cuX2xvZ1NlcnZpY2UuZGVidWcoIlVua25vd24gRVhFQ1VURSBjb2RlOiAiLHtjb2RlOmV9KX0pKSxnLl9wYXJzZXIuc2V0T3NjSGFuZGxlckZhbGxiYWNrKChmdW5jdGlvbihlLHQscil7Zy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBPU0MgY29kZTogIix7aWRlbnRpZmllcjplLGFjdGlvbjp0LGRhdGE6cn0pfSkpLGcuX3BhcnNlci5zZXREY3NIYW5kbGVyRmFsbGJhY2soKGZ1bmN0aW9uKGUsdCxyKXsiSE9PSyI9PT10JiYocj1yLnRvQXJyYXkoKSksZy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBEQ1MgY29kZTogIix7aWRlbnRpZmllcjpnLl9wYXJzZXIuaWRlbnRUb1N0cmluZyhlKSxhY3Rpb246dCxwYXlsb2FkOnJ9KX0pKSxnLl9wYXJzZXIuc2V0UHJpbnRIYW5kbGVyKChmdW5jdGlvbihlLHQscil7cmV0dXJuIGcucHJpbnQoZSx0LHIpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJAIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmluc2VydENoYXJzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiAiLGZpbmFsOiJAIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNjcm9sbExlZnQoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IkEifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuY3Vyc29yVXAoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiICIsZmluYWw6IkEifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc2Nyb2xsUmlnaHQoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IkIifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuY3Vyc29yRG93bihlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiQyJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5jdXJzb3JGb3J3YXJkKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJEIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvckJhY2t3YXJkKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJFIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvck5leHRMaW5lKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJGIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvclByZWNlZGluZ0xpbmUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IkcifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuY3Vyc29yQ2hhckFic29sdXRlKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJIIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvclBvc2l0aW9uKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJJIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvckZvcndhcmRUYWIoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IkoifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuZXJhc2VJbkRpc3BsYXkoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7cHJlZml4OiI/IixmaW5hbDoiSiJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5lcmFzZUluRGlzcGxheShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiSyJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5lcmFzZUluTGluZShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtwcmVmaXg6Ij8iLGZpbmFsOiJLIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmVyYXNlSW5MaW5lKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJMIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmluc2VydExpbmVzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJNIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRlbGV0ZUxpbmVzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJQIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRlbGV0ZUNoYXJzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJTIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNjcm9sbFVwKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJUIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNjcm9sbERvd24oZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IlgifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuZXJhc2VDaGFycyhlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiWiJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5jdXJzb3JCYWNrd2FyZFRhYihlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiYCJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5jaGFyUG9zQWJzb2x1dGUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6ImEifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuaFBvc2l0aW9uUmVsYXRpdmUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6ImIifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcucmVwZWF0UHJlY2VkaW5nQ2hhcmFjdGVyKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJjIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNlbmREZXZpY2VBdHRyaWJ1dGVzUHJpbWFyeShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtwcmVmaXg6Ij4iLGZpbmFsOiJjIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNlbmREZXZpY2VBdHRyaWJ1dGVzU2Vjb25kYXJ5KGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJkIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmxpbmVQb3NBYnNvbHV0ZShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiZSJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy52UG9zaXRpb25SZWxhdGl2ZShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiZiJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5oVlBvc2l0aW9uKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJnIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnRhYkNsZWFyKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJoIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldE1vZGUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7cHJlZml4OiI/IixmaW5hbDoiaCJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5zZXRNb2RlUHJpdmF0ZShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoibCJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5yZXNldE1vZGUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7cHJlZml4OiI/IixmaW5hbDoibCJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5yZXNldE1vZGVQcml2YXRlKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJtIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmNoYXJBdHRyaWJ1dGVzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJuIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRldmljZVN0YXR1cyhlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtwcmVmaXg6Ij8iLGZpbmFsOiJuIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRldmljZVN0YXR1c1ByaXZhdGUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiISIsZmluYWw6InAifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc29mdFJlc2V0KGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiAiLGZpbmFsOiJxIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldEN1cnNvclN0eWxlKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJyIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldFNjcm9sbFJlZ2lvbihlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoicyJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5zYXZlQ3Vyc29yKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJ0In0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLndpbmRvd09wdGlvbnMoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6InUifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcucmVzdG9yZUN1cnNvcihlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtpbnRlcm1lZGlhdGVzOiInIixmaW5hbDoifSJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5pbnNlcnRDb2x1bW5zKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiciLGZpbmFsOiJ+In0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRlbGV0ZUNvbHVtbnMoZSl9KSksZy5fcGFyc2VyLnNldEV4ZWN1dGVIYW5kbGVyKHMuQzAuQkVMLChmdW5jdGlvbigpe3JldHVybiBnLmJlbGwoKX0pKSxnLl9wYXJzZXIuc2V0RXhlY3V0ZUhhbmRsZXIocy5DMC5MRiwoZnVuY3Rpb24oKXtyZXR1cm4gZy5saW5lRmVlZCgpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMwLlZULChmdW5jdGlvbigpe3JldHVybiBnLmxpbmVGZWVkKCl9KSksZy5fcGFyc2VyLnNldEV4ZWN1dGVIYW5kbGVyKHMuQzAuRkYsKGZ1bmN0aW9uKCl7cmV0dXJuIGcubGluZUZlZWQoKX0pKSxnLl9wYXJzZXIuc2V0RXhlY3V0ZUhhbmRsZXIocy5DMC5DUiwoZnVuY3Rpb24oKXtyZXR1cm4gZy5jYXJyaWFnZVJldHVybigpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMwLkJTLChmdW5jdGlvbigpe3JldHVybiBnLmJhY2tzcGFjZSgpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMwLkhULChmdW5jdGlvbigpe3JldHVybiBnLnRhYigpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMwLlNPLChmdW5jdGlvbigpe3JldHVybiBnLnNoaWZ0T3V0KCl9KSksZy5fcGFyc2VyLnNldEV4ZWN1dGVIYW5kbGVyKHMuQzAuU0ksKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2hpZnRJbigpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMxLklORCwoZnVuY3Rpb24oKXtyZXR1cm4gZy5pbmRleCgpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMxLk5FTCwoZnVuY3Rpb24oKXtyZXR1cm4gZy5uZXh0TGluZSgpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMxLkhUUywoZnVuY3Rpb24oKXtyZXR1cm4gZy50YWJTZXQoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKDAsbmV3IHkuT3NjSGFuZGxlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc2V0VGl0bGUoZSksZy5zZXRJY29uTmFtZShlKSwhMH0pKSksZy5fcGFyc2VyLnJlZ2lzdGVyT3NjSGFuZGxlcigxLG5ldyB5Lk9zY0hhbmRsZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldEljb25OYW1lKGUpfSkpKSxnLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKDIsbmV3IHkuT3NjSGFuZGxlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc2V0VGl0bGUoZSl9KSkpLGcuX3BhcnNlci5yZWdpc3Rlck9zY0hhbmRsZXIoNCxuZXcgeS5Pc2NIYW5kbGVyKChmdW5jdGlvbihlKXtyZXR1cm4gZy5zZXRPclJlcG9ydEluZGV4ZWRDb2xvcihlKX0pKSksZy5fcGFyc2VyLnJlZ2lzdGVyT3NjSGFuZGxlcigxMCxuZXcgeS5Pc2NIYW5kbGVyKChmdW5jdGlvbihlKXtyZXR1cm4gZy5zZXRPclJlcG9ydEZnQ29sb3IoZSl9KSkpLGcuX3BhcnNlci5yZWdpc3Rlck9zY0hhbmRsZXIoMTEsbmV3IHkuT3NjSGFuZGxlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc2V0T3JSZXBvcnRCZ0NvbG9yKGUpfSkpKSxnLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKDEyLG5ldyB5Lk9zY0hhbmRsZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldE9yUmVwb3J0Q3Vyc29yQ29sb3IoZSl9KSkpLGcuX3BhcnNlci5yZWdpc3Rlck9zY0hhbmRsZXIoMTA0LG5ldyB5Lk9zY0hhbmRsZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBnLnJlc3RvcmVJbmRleGVkQ29sb3IoZSl9KSkpLGcuX3BhcnNlci5yZWdpc3Rlck9zY0hhbmRsZXIoMTEwLG5ldyB5Lk9zY0hhbmRsZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBnLnJlc3RvcmVGZ0NvbG9yKGUpfSkpKSxnLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKDExMSxuZXcgeS5Pc2NIYW5kbGVyKChmdW5jdGlvbihlKXtyZXR1cm4gZy5yZXN0b3JlQmdDb2xvcihlKX0pKSksZy5fcGFyc2VyLnJlZ2lzdGVyT3NjSGFuZGxlcigxMTIsbmV3IHkuT3NjSGFuZGxlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGcucmVzdG9yZUN1cnNvckNvbG9yKGUpfSkpKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiNyJ9LChmdW5jdGlvbigpe3JldHVybiBnLnNhdmVDdXJzb3IoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiOCJ9LChmdW5jdGlvbigpe3JldHVybiBnLnJlc3RvcmVDdXJzb3IoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiRCJ9LChmdW5jdGlvbigpe3JldHVybiBnLmluZGV4KCl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7ZmluYWw6IkUifSwoZnVuY3Rpb24oKXtyZXR1cm4gZy5uZXh0TGluZSgpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJIIn0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcudGFiU2V0KCl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7ZmluYWw6Ik0ifSwoZnVuY3Rpb24oKXtyZXR1cm4gZy5yZXZlcnNlSW5kZXgoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiPSJ9LChmdW5jdGlvbigpe3JldHVybiBnLmtleXBhZEFwcGxpY2F0aW9uTW9kZSgpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiI+In0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcua2V5cGFkTnVtZXJpY01vZGUoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiYyJ9LChmdW5jdGlvbigpe3JldHVybiBnLmZ1bGxSZXNldCgpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJuIn0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDIpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJvIn0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDMpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJ8In0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDMpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJ9In0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDIpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJ+In0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDEpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiUiLGZpbmFsOiJAIn0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0RGVmYXVsdENoYXJzZXQoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtpbnRlcm1lZGlhdGVzOiIlIixmaW5hbDoiRyJ9LChmdW5jdGlvbigpe3JldHVybiBnLnNlbGVjdERlZmF1bHRDaGFyc2V0KCl9KSk7dmFyIG09ZnVuY3Rpb24oZSl7Yi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiKCIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiKCIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiKSIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiKSIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiKiIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiKiIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiKyIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiKyIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiLSIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiLSIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiLiIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiLiIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiLyIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiLyIrZSl9KSl9LGI9dGhpcztmb3IodmFyIFMgaW4gYS5DSEFSU0VUUyltKFMpO3JldHVybiBnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtpbnRlcm1lZGlhdGVzOiIjIixmaW5hbDoiOCJ9LChmdW5jdGlvbigpe3JldHVybiBnLnNjcmVlbkFsaWdubWVudFBhdHRlcm4oKX0pKSxnLl9wYXJzZXIuc2V0RXJyb3JIYW5kbGVyKChmdW5jdGlvbihlKXtyZXR1cm4gZy5fbG9nU2VydmljZS5lcnJvcigiUGFyc2luZyBlcnJvcjogIixlKSxlfSkpLGcuX3BhcnNlci5yZWdpc3RlckRjc0hhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiQiLGZpbmFsOiJxIn0sbmV3IEwoZy5fYnVmZmVyU2VydmljZSxnLl9jb3JlU2VydmljZSxnLl9sb2dTZXJ2aWNlLGcuX29wdGlvbnNTZXJ2aWNlKSksZ31yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0QmVsbCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RCZWxsLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0UmVmcmVzaFJvd3MiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZXF1ZXN0UmVmcmVzaFJvd3MuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblJlcXVlc3RSZXNldCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RSZXNldC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uUmVxdWVzdFNlbmRGb2N1cyIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RTZW5kRm9jdXMuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblJlcXVlc3RTeW5jU2Nyb2xsQmFyIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblJlcXVlc3RXaW5kb3dzT3B0aW9uc1JlcG9ydCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RXaW5kb3dzT3B0aW9uc1JlcG9ydC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQTExeUNoYXIiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25BMTF5Q2hhci5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQTExeVRhYiIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkExMXlUYWIuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkN1cnNvck1vdmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25DdXJzb3JNb3ZlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25MaW5lRmVlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkxpbmVGZWVkLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25TY3JvbGwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25TY3JvbGwuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblRpdGxlQ2hhbmdlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uVGl0bGVDaGFuZ2UuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkNvbG9yIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uQ29sb3IuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe2UucHJvdG90eXBlLmRpc3Bvc2UuY2FsbCh0aGlzKX0sdC5wcm90b3R5cGUuX3ByZXNlcnZlU3RhY2s9ZnVuY3Rpb24oZSx0LHIsaSl7dGhpcy5fcGFyc2VTdGFjay5wYXVzZWQ9ITAsdGhpcy5fcGFyc2VTdGFjay5jdXJzb3JTdGFydFg9ZSx0aGlzLl9wYXJzZVN0YWNrLmN1cnNvclN0YXJ0WT10LHRoaXMuX3BhcnNlU3RhY2suZGVjb2RlZExlbmd0aD1yLHRoaXMuX3BhcnNlU3RhY2sucG9zaXRpb249aX0sdC5wcm90b3R5cGUuX2xvZ1Nsb3dSZXNvbHZpbmdBc3luYz1mdW5jdGlvbihlKXt0aGlzLl9sb2dTZXJ2aWNlLmxvZ0xldmVsPD1nLkxvZ0xldmVsRW51bS5XQVJOJiZQcm9taXNlLnJhY2UoW2UsbmV3IFByb21pc2UoKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIHQoIiNTTE9XX1RJTUVPVVQiKX0pLDVlMyl9KSldKS5jYXRjaCgoZnVuY3Rpb24oZSl7aWYoIiNTTE9XX1RJTUVPVVQiIT09ZSl0aHJvdyBlO2NvbnNvbGUud2FybigiYXN5bmMgcGFyc2VyIGhhbmRsZXIgdGFraW5nIGxvbmdlciB0aGFuIDUwMDAgbXMiKX0pKX0sdC5wcm90b3R5cGUucGFyc2U9ZnVuY3Rpb24oZSx0KXt2YXIgcixpPXRoaXMuX2FjdGl2ZUJ1ZmZlci54LG49dGhpcy5fYWN0aXZlQnVmZmVyLnksbz0wLHM9dGhpcy5fcGFyc2VTdGFjay5wYXVzZWQ7aWYocyl7aWYocj10aGlzLl9wYXJzZXIucGFyc2UodGhpcy5fcGFyc2VCdWZmZXIsdGhpcy5fcGFyc2VTdGFjay5kZWNvZGVkTGVuZ3RoLHQpKXJldHVybiB0aGlzLl9sb2dTbG93UmVzb2x2aW5nQXN5bmMocikscjtpPXRoaXMuX3BhcnNlU3RhY2suY3Vyc29yU3RhcnRYLG49dGhpcy5fcGFyc2VTdGFjay5jdXJzb3JTdGFydFksdGhpcy5fcGFyc2VTdGFjay5wYXVzZWQ9ITEsZS5sZW5ndGg+QyYmKG89dGhpcy5fcGFyc2VTdGFjay5wb3NpdGlvbitDKX1pZih0aGlzLl9sb2dTZXJ2aWNlLmxvZ0xldmVsPD1nLkxvZ0xldmVsRW51bS5ERUJVRyYmdGhpcy5fbG9nU2VydmljZS5kZWJ1ZygicGFyc2luZyBkYXRhIisoInN0cmluZyI9PXR5cGVvZiBlPycgIicrZSsnIic6IiIpLCJzdHJpbmciPT10eXBlb2YgZT9lLnNwbGl0KCIiKS5tYXAoKGZ1bmN0aW9uKGUpe3JldHVybiBlLmNoYXJDb2RlQXQoMCl9KSk6ZSksdGhpcy5fcGFyc2VCdWZmZXIubGVuZ3RoPGUubGVuZ3RoJiZ0aGlzLl9wYXJzZUJ1ZmZlci5sZW5ndGg8QyYmKHRoaXMuX3BhcnNlQnVmZmVyPW5ldyBVaW50MzJBcnJheShNYXRoLm1pbihlLmxlbmd0aCxDKSkpLHN8fHRoaXMuX2RpcnR5Um93U2VydmljZS5jbGVhclJhbmdlKCksZS5sZW5ndGg+Qylmb3IodmFyIGE9bzthPGUubGVuZ3RoO2ErPUMpe3ZhciBjPWErQzxlLmxlbmd0aD9hK0M6ZS5sZW5ndGgsbD0ic3RyaW5nIj09dHlwZW9mIGU/dGhpcy5fc3RyaW5nRGVjb2Rlci5kZWNvZGUoZS5zdWJzdHJpbmcoYSxjKSx0aGlzLl9wYXJzZUJ1ZmZlcik6dGhpcy5fdXRmOERlY29kZXIuZGVjb2RlKGUuc3ViYXJyYXkoYSxjKSx0aGlzLl9wYXJzZUJ1ZmZlcik7aWYocj10aGlzLl9wYXJzZXIucGFyc2UodGhpcy5fcGFyc2VCdWZmZXIsbCkpcmV0dXJuIHRoaXMuX3ByZXNlcnZlU3RhY2soaSxuLGwsYSksdGhpcy5fbG9nU2xvd1Jlc29sdmluZ0FzeW5jKHIpLHJ9ZWxzZSBpZighcyYmKGw9InN0cmluZyI9PXR5cGVvZiBlP3RoaXMuX3N0cmluZ0RlY29kZXIuZGVjb2RlKGUsdGhpcy5fcGFyc2VCdWZmZXIpOnRoaXMuX3V0ZjhEZWNvZGVyLmRlY29kZShlLHRoaXMuX3BhcnNlQnVmZmVyKSxyPXRoaXMuX3BhcnNlci5wYXJzZSh0aGlzLl9wYXJzZUJ1ZmZlcixsKSkpcmV0dXJuIHRoaXMuX3ByZXNlcnZlU3RhY2soaSxuLGwsMCksdGhpcy5fbG9nU2xvd1Jlc29sdmluZ0FzeW5jKHIpLHI7dGhpcy5fYWN0aXZlQnVmZmVyLng9PT1pJiZ0aGlzLl9hY3RpdmVCdWZmZXIueT09PW58fHRoaXMuX29uQ3Vyc29yTW92ZS5maXJlKCksdGhpcy5fb25SZXF1ZXN0UmVmcmVzaFJvd3MuZmlyZSh0aGlzLl9kaXJ0eVJvd1NlcnZpY2Uuc3RhcnQsdGhpcy5fZGlydHlSb3dTZXJ2aWNlLmVuZCl9LHQucHJvdG90eXBlLnByaW50PWZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuLG89dGhpcy5fY2hhcnNldFNlcnZpY2UuY2hhcnNldCxzPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuc2NyZWVuUmVhZGVyTW9kZSxhPXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyxjPXRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy53cmFwYXJvdW5kLGw9dGhpcy5fY29yZVNlcnZpY2UubW9kZXMuaW5zZXJ0TW9kZSx1PXRoaXMuX2N1ckF0dHJEYXRhLGY9dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkpO3RoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnkpLHRoaXMuX2FjdGl2ZUJ1ZmZlci54JiZyLXQ+MCYmMj09PWYuZ2V0V2lkdGgodGhpcy5fYWN0aXZlQnVmZmVyLngtMSkmJmYuc2V0Q2VsbEZyb21Db2RlUG9pbnQodGhpcy5fYWN0aXZlQnVmZmVyLngtMSwwLDEsdS5mZyx1LmJnLHUuZXh0ZW5kZWQpO2Zvcih2YXIgXz10O188cjsrK18pe2lmKGk9ZVtfXSxuPXRoaXMuX3VuaWNvZGVTZXJ2aWNlLndjd2lkdGgoaSksaTwxMjcmJm8pe3ZhciBwPW9bU3RyaW5nLmZyb21DaGFyQ29kZShpKV07cCYmKGk9cC5jaGFyQ29kZUF0KDApKX1pZihzJiZ0aGlzLl9vbkExMXlDaGFyLmZpcmUoKDAsaC5zdHJpbmdGcm9tQ29kZVBvaW50KShpKSksbnx8IXRoaXMuX2FjdGl2ZUJ1ZmZlci54KXtpZih0aGlzLl9hY3RpdmVCdWZmZXIueCtuLTE+PWEpaWYoYyl7Zm9yKDt0aGlzLl9hY3RpdmVCdWZmZXIueDxhOylmLnNldENlbGxGcm9tQ29kZVBvaW50KHRoaXMuX2FjdGl2ZUJ1ZmZlci54KyssMCwxLHUuZmcsdS5iZyx1LmV4dGVuZGVkKTt0aGlzLl9hY3RpdmVCdWZmZXIueD0wLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KyssdGhpcy5fYWN0aXZlQnVmZmVyLnk9PT10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tKzE/KHRoaXMuX2FjdGl2ZUJ1ZmZlci55LS0sdGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGwodGhpcy5fZXJhc2VBdHRyRGF0YSgpLCEwKSk6KHRoaXMuX2FjdGl2ZUJ1ZmZlci55Pj10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MmJih0aGlzLl9hY3RpdmVCdWZmZXIueT10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSksdGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkpLmlzV3JhcHBlZD0hMCksZj10aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuZ2V0KHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZSt0aGlzLl9hY3RpdmVCdWZmZXIueSl9ZWxzZSBpZih0aGlzLl9hY3RpdmVCdWZmZXIueD1hLTEsMj09PW4pY29udGludWU7aWYobCYmKGYuaW5zZXJ0Q2VsbHModGhpcy5fYWN0aXZlQnVmZmVyLngsbix0aGlzLl9hY3RpdmVCdWZmZXIuZ2V0TnVsbENlbGwodSksdSksMj09PWYuZ2V0V2lkdGgoYS0xKSYmZi5zZXRDZWxsRnJvbUNvZGVQb2ludChhLTEsZC5OVUxMX0NFTExfQ09ERSxkLk5VTExfQ0VMTF9XSURUSCx1LmZnLHUuYmcsdS5leHRlbmRlZCkpLGYuc2V0Q2VsbEZyb21Db2RlUG9pbnQodGhpcy5fYWN0aXZlQnVmZmVyLngrKyxpLG4sdS5mZyx1LmJnLHUuZXh0ZW5kZWQpLG4+MClmb3IoOy0tbjspZi5zZXRDZWxsRnJvbUNvZGVQb2ludCh0aGlzLl9hY3RpdmVCdWZmZXIueCsrLDAsMCx1LmZnLHUuYmcsdS5leHRlbmRlZCl9ZWxzZSBmLmdldFdpZHRoKHRoaXMuX2FjdGl2ZUJ1ZmZlci54LTEpP2YuYWRkQ29kZXBvaW50VG9DZWxsKHRoaXMuX2FjdGl2ZUJ1ZmZlci54LTEsaSk6Zi5hZGRDb2RlcG9pbnRUb0NlbGwodGhpcy5fYWN0aXZlQnVmZmVyLngtMixpKX1yLXQ+MCYmKGYubG9hZENlbGwodGhpcy5fYWN0aXZlQnVmZmVyLngtMSx0aGlzLl93b3JrQ2VsbCksMj09PXRoaXMuX3dvcmtDZWxsLmdldFdpZHRoKCl8fHRoaXMuX3dvcmtDZWxsLmdldENvZGUoKT42NTUzNT90aGlzLl9wYXJzZXIucHJlY2VkaW5nQ29kZXBvaW50PTA6dGhpcy5fd29ya0NlbGwuaXNDb21iaW5lZCgpP3RoaXMuX3BhcnNlci5wcmVjZWRpbmdDb2RlcG9pbnQ9dGhpcy5fd29ya0NlbGwuZ2V0Q2hhcnMoKS5jaGFyQ29kZUF0KDApOnRoaXMuX3BhcnNlci5wcmVjZWRpbmdDb2RlcG9pbnQ9dGhpcy5fd29ya0NlbGwuY29udGVudCksdGhpcy5fYWN0aXZlQnVmZmVyLng8YSYmci10PjAmJjA9PT1mLmdldFdpZHRoKHRoaXMuX2FjdGl2ZUJ1ZmZlci54KSYmIWYuaGFzQ29udGVudCh0aGlzLl9hY3RpdmVCdWZmZXIueCkmJmYuc2V0Q2VsbEZyb21Db2RlUG9pbnQodGhpcy5fYWN0aXZlQnVmZmVyLngsMCwxLHUuZmcsdS5iZyx1LmV4dGVuZGVkKSx0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci55KX0sdC5wcm90b3R5cGUucmVnaXN0ZXJDc2lIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcztyZXR1cm4idCIhPT1lLmZpbmFsfHxlLnByZWZpeHx8ZS5pbnRlcm1lZGlhdGVzP3RoaXMuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoZSx0KTp0aGlzLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKGUsKGZ1bmN0aW9uKGUpe3JldHVybiF3KGUucGFyYW1zWzBdLHIuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMud2luZG93T3B0aW9ucyl8fHQoZSl9KSl9LHQucHJvdG90eXBlLnJlZ2lzdGVyRGNzSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9wYXJzZXIucmVnaXN0ZXJEY3NIYW5kbGVyKGUsbmV3IG0uRGNzSGFuZGxlcih0KSl9LHQucHJvdG90eXBlLnJlZ2lzdGVyRXNjSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKGUsdCl9LHQucHJvdG90eXBlLnJlZ2lzdGVyT3NjSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKGUsbmV3IHkuT3NjSGFuZGxlcih0KSl9LHQucHJvdG90eXBlLmJlbGw9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZXF1ZXN0QmVsbC5maXJlKCksITB9LHQucHJvdG90eXBlLmxpbmVGZWVkPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnkpLHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY29udmVydEVvbCYmKHRoaXMuX2FjdGl2ZUJ1ZmZlci54PTApLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KyssdGhpcy5fYWN0aXZlQnVmZmVyLnk9PT10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tKzE/KHRoaXMuX2FjdGl2ZUJ1ZmZlci55LS0sdGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGwodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSk6dGhpcy5fYWN0aXZlQnVmZmVyLnk+PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyYmKHRoaXMuX2FjdGl2ZUJ1ZmZlci55PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xKSx0aGlzLl9hY3RpdmVCdWZmZXIueD49dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzJiZ0aGlzLl9hY3RpdmVCdWZmZXIueC0tLHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnkpLHRoaXMuX29uTGluZUZlZWQuZmlyZSgpLCEwfSx0LnByb3RvdHlwZS5jYXJyaWFnZVJldHVybj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9hY3RpdmVCdWZmZXIueD0wLCEwfSx0LnByb3RvdHlwZS5iYWNrc3BhY2U9ZnVuY3Rpb24oKXt2YXIgZTtpZighdGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLnJldmVyc2VXcmFwYXJvdW5kKXJldHVybiB0aGlzLl9yZXN0cmljdEN1cnNvcigpLHRoaXMuX2FjdGl2ZUJ1ZmZlci54PjAmJnRoaXMuX2FjdGl2ZUJ1ZmZlci54LS0sITA7aWYodGhpcy5fcmVzdHJpY3RDdXJzb3IodGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSx0aGlzLl9hY3RpdmVCdWZmZXIueD4wKXRoaXMuX2FjdGl2ZUJ1ZmZlci54LS07ZWxzZSBpZigwPT09dGhpcy5fYWN0aXZlQnVmZmVyLngmJnRoaXMuX2FjdGl2ZUJ1ZmZlci55PnRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3AmJnRoaXMuX2FjdGl2ZUJ1ZmZlci55PD10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tJiYobnVsbD09PShlPXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55KSl8fHZvaWQgMD09PWU/dm9pZCAwOmUuaXNXcmFwcGVkKSl7dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkpLmlzV3JhcHBlZD0hMSx0aGlzLl9hY3RpdmVCdWZmZXIueS0tLHRoaXMuX2FjdGl2ZUJ1ZmZlci54PXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scy0xO3ZhciB0PXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55KTt0Lmhhc1dpZHRoKHRoaXMuX2FjdGl2ZUJ1ZmZlci54KSYmIXQuaGFzQ29udGVudCh0aGlzLl9hY3RpdmVCdWZmZXIueCkmJnRoaXMuX2FjdGl2ZUJ1ZmZlci54LS19cmV0dXJuIHRoaXMuX3Jlc3RyaWN0Q3Vyc29yKCksITB9LHQucHJvdG90eXBlLnRhYj1mdW5jdGlvbigpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci54Pj10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpcmV0dXJuITA7dmFyIGU9dGhpcy5fYWN0aXZlQnVmZmVyLng7cmV0dXJuIHRoaXMuX2FjdGl2ZUJ1ZmZlci54PXRoaXMuX2FjdGl2ZUJ1ZmZlci5uZXh0U3RvcCgpLHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuc2NyZWVuUmVhZGVyTW9kZSYmdGhpcy5fb25BMTF5VGFiLmZpcmUodGhpcy5fYWN0aXZlQnVmZmVyLngtZSksITB9LHQucHJvdG90eXBlLnNoaWZ0T3V0PWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NoYXJzZXRTZXJ2aWNlLnNldGdMZXZlbCgxKSwhMH0sdC5wcm90b3R5cGUuc2hpZnRJbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnTGV2ZWwoMCksITB9LHQucHJvdG90eXBlLl9yZXN0cmljdEN1cnNvcj1mdW5jdGlvbihlKXt2b2lkIDA9PT1lJiYoZT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtMSksdGhpcy5fYWN0aXZlQnVmZmVyLng9TWF0aC5taW4oZSxNYXRoLm1heCgwLHRoaXMuX2FjdGl2ZUJ1ZmZlci54KSksdGhpcy5fYWN0aXZlQnVmZmVyLnk9dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLm9yaWdpbj9NYXRoLm1pbih0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tLE1hdGgubWF4KHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3AsdGhpcy5fYWN0aXZlQnVmZmVyLnkpKTpNYXRoLm1pbih0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSxNYXRoLm1heCgwLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KSksdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIueSl9LHQucHJvdG90eXBlLl9zZXRDdXJzb3I9ZnVuY3Rpb24oZSx0KXt0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci55KSx0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMub3JpZ2luPyh0aGlzLl9hY3RpdmVCdWZmZXIueD1lLHRoaXMuX2FjdGl2ZUJ1ZmZlci55PXRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3ArdCk6KHRoaXMuX2FjdGl2ZUJ1ZmZlci54PWUsdGhpcy5fYWN0aXZlQnVmZmVyLnk9dCksdGhpcy5fcmVzdHJpY3RDdXJzb3IoKSx0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci55KX0sdC5wcm90b3R5cGUuX21vdmVDdXJzb3I9ZnVuY3Rpb24oZSx0KXt0aGlzLl9yZXN0cmljdEN1cnNvcigpLHRoaXMuX3NldEN1cnNvcih0aGlzLl9hY3RpdmVCdWZmZXIueCtlLHRoaXMuX2FjdGl2ZUJ1ZmZlci55K3QpfSx0LnByb3RvdHlwZS5jdXJzb3JVcD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9hY3RpdmVCdWZmZXIueS10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wO3JldHVybiB0Pj0wP3RoaXMuX21vdmVDdXJzb3IoMCwtTWF0aC5taW4odCxlLnBhcmFtc1swXXx8MSkpOnRoaXMuX21vdmVDdXJzb3IoMCwtKGUucGFyYW1zWzBdfHwxKSksITB9LHQucHJvdG90eXBlLmN1cnNvckRvd249ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbS10aGlzLl9hY3RpdmVCdWZmZXIueTtyZXR1cm4gdD49MD90aGlzLl9tb3ZlQ3Vyc29yKDAsTWF0aC5taW4odCxlLnBhcmFtc1swXXx8MSkpOnRoaXMuX21vdmVDdXJzb3IoMCxlLnBhcmFtc1swXXx8MSksITB9LHQucHJvdG90eXBlLmN1cnNvckZvcndhcmQ9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX21vdmVDdXJzb3IoZS5wYXJhbXNbMF18fDEsMCksITB9LHQucHJvdG90eXBlLmN1cnNvckJhY2t3YXJkPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9tb3ZlQ3Vyc29yKC0oZS5wYXJhbXNbMF18fDEpLDApLCEwfSx0LnByb3RvdHlwZS5jdXJzb3JOZXh0TGluZT1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5jdXJzb3JEb3duKGUpLHRoaXMuX2FjdGl2ZUJ1ZmZlci54PTAsITB9LHQucHJvdG90eXBlLmN1cnNvclByZWNlZGluZ0xpbmU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuY3Vyc29yVXAoZSksdGhpcy5fYWN0aXZlQnVmZmVyLng9MCwhMH0sdC5wcm90b3R5cGUuY3Vyc29yQ2hhckFic29sdXRlPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9zZXRDdXJzb3IoKGUucGFyYW1zWzBdfHwxKS0xLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KSwhMH0sdC5wcm90b3R5cGUuY3Vyc29yUG9zaXRpb249ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX3NldEN1cnNvcihlLmxlbmd0aD49Mj8oZS5wYXJhbXNbMV18fDEpLTE6MCwoZS5wYXJhbXNbMF18fDEpLTEpLCEwfSx0LnByb3RvdHlwZS5jaGFyUG9zQWJzb2x1dGU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX3NldEN1cnNvcigoZS5wYXJhbXNbMF18fDEpLTEsdGhpcy5fYWN0aXZlQnVmZmVyLnkpLCEwfSx0LnByb3RvdHlwZS5oUG9zaXRpb25SZWxhdGl2ZT1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fbW92ZUN1cnNvcihlLnBhcmFtc1swXXx8MSwwKSwhMH0sdC5wcm90b3R5cGUubGluZVBvc0Fic29sdXRlPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9zZXRDdXJzb3IodGhpcy5fYWN0aXZlQnVmZmVyLngsKGUucGFyYW1zWzBdfHwxKS0xKSwhMH0sdC5wcm90b3R5cGUudlBvc2l0aW9uUmVsYXRpdmU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX21vdmVDdXJzb3IoMCxlLnBhcmFtc1swXXx8MSksITB9LHQucHJvdG90eXBlLmhWUG9zaXRpb249ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuY3Vyc29yUG9zaXRpb24oZSksITB9LHQucHJvdG90eXBlLnRhYkNsZWFyPWZ1bmN0aW9uKGUpe3ZhciB0PWUucGFyYW1zWzBdO3JldHVybiAwPT09dD9kZWxldGUgdGhpcy5fYWN0aXZlQnVmZmVyLnRhYnNbdGhpcy5fYWN0aXZlQnVmZmVyLnhdOjM9PT10JiYodGhpcy5fYWN0aXZlQnVmZmVyLnRhYnM9e30pLCEwfSx0LnByb3RvdHlwZS5jdXJzb3JGb3J3YXJkVGFiPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci54Pj10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpcmV0dXJuITA7Zm9yKHZhciB0PWUucGFyYW1zWzBdfHwxO3QtLTspdGhpcy5fYWN0aXZlQnVmZmVyLng9dGhpcy5fYWN0aXZlQnVmZmVyLm5leHRTdG9wKCk7cmV0dXJuITB9LHQucHJvdG90eXBlLmN1cnNvckJhY2t3YXJkVGFiPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci54Pj10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpcmV0dXJuITA7Zm9yKHZhciB0PWUucGFyYW1zWzBdfHwxO3QtLTspdGhpcy5fYWN0aXZlQnVmZmVyLng9dGhpcy5fYWN0aXZlQnVmZmVyLnByZXZTdG9wKCk7cmV0dXJuITB9LHQucHJvdG90eXBlLl9lcmFzZUluQnVmZmVyTGluZT1mdW5jdGlvbihlLHQscixpKXt2b2lkIDA9PT1pJiYoaT0hMSk7dmFyIG49dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrZSk7bi5yZXBsYWNlQ2VsbHModCxyLHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaSYmKG4uaXNXcmFwcGVkPSExKX0sdC5wcm90b3R5cGUuX3Jlc2V0QnVmZmVyTGluZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuZ2V0KHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZStlKTt0LmZpbGwodGhpcy5fYWN0aXZlQnVmZmVyLmdldE51bGxDZWxsKHRoaXMuX2VyYXNlQXR0ckRhdGEoKSkpLHQuaXNXcmFwcGVkPSExfSx0LnByb3RvdHlwZS5lcmFzZUluRGlzcGxheT1mdW5jdGlvbihlKXt2YXIgdDtzd2l0Y2godGhpcy5fcmVzdHJpY3RDdXJzb3IodGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSxlLnBhcmFtc1swXSl7Y2FzZSAwOmZvcih0PXRoaXMuX2FjdGl2ZUJ1ZmZlci55LHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodCksdGhpcy5fZXJhc2VJbkJ1ZmZlckxpbmUodCsrLHRoaXMuX2FjdGl2ZUJ1ZmZlci54LHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scywwPT09dGhpcy5fYWN0aXZlQnVmZmVyLngpO3Q8dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzO3QrKyl0aGlzLl9yZXNldEJ1ZmZlckxpbmUodCk7dGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0KTticmVhaztjYXNlIDE6Zm9yKHQ9dGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0KSx0aGlzLl9lcmFzZUluQnVmZmVyTGluZSh0LDAsdGhpcy5fYWN0aXZlQnVmZmVyLngrMSwhMCksdGhpcy5fYWN0aXZlQnVmZmVyLngrMT49dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzJiYodGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0KzEpLmlzV3JhcHBlZD0hMSk7dC0tOyl0aGlzLl9yZXNldEJ1ZmZlckxpbmUodCk7dGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSgwKTticmVhaztjYXNlIDI6Zm9yKHQ9dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodC0xKTt0LS07KXRoaXMuX3Jlc2V0QnVmZmVyTGluZSh0KTt0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KDApO2JyZWFrO2Nhc2UgMzp2YXIgcj10aGlzLl9hY3RpdmVCdWZmZXIubGluZXMubGVuZ3RoLXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cztyPjAmJih0aGlzLl9hY3RpdmVCdWZmZXIubGluZXMudHJpbVN0YXJ0KHIpLHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZT1NYXRoLm1heCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UtciwwKSx0aGlzLl9hY3RpdmVCdWZmZXIueWRpc3A9TWF0aC5tYXgodGhpcy5fYWN0aXZlQnVmZmVyLnlkaXNwLXIsMCksdGhpcy5fb25TY3JvbGwuZmlyZSgwKSl9cmV0dXJuITB9LHQucHJvdG90eXBlLmVyYXNlSW5MaW5lPWZ1bmN0aW9uKGUpe3N3aXRjaCh0aGlzLl9yZXN0cmljdEN1cnNvcih0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpLGUucGFyYW1zWzBdKXtjYXNlIDA6dGhpcy5fZXJhc2VJbkJ1ZmZlckxpbmUodGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fYWN0aXZlQnVmZmVyLngsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLDA9PT10aGlzLl9hY3RpdmVCdWZmZXIueCk7YnJlYWs7Y2FzZSAxOnRoaXMuX2VyYXNlSW5CdWZmZXJMaW5lKHRoaXMuX2FjdGl2ZUJ1ZmZlci55LDAsdGhpcy5fYWN0aXZlQnVmZmVyLngrMSwhMSk7YnJlYWs7Y2FzZSAyOnRoaXMuX2VyYXNlSW5CdWZmZXJMaW5lKHRoaXMuX2FjdGl2ZUJ1ZmZlci55LDAsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLCEwKX1yZXR1cm4gdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIueSksITB9LHQucHJvdG90eXBlLmluc2VydExpbmVzPWZ1bmN0aW9uKGUpe3RoaXMuX3Jlc3RyaWN0Q3Vyc29yKCk7dmFyIHQ9ZS5wYXJhbXNbMF18fDE7aWYodGhpcy5fYWN0aXZlQnVmZmVyLnk+dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbXx8dGhpcy5fYWN0aXZlQnVmZmVyLnk8dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcClyZXR1cm4hMDtmb3IodmFyIHI9dGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55LGk9dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEtdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSxuPXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xK3RoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZS1pKzE7dC0tOyl0aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuc3BsaWNlKG4tMSwxKSx0aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuc3BsaWNlKHIsMCx0aGlzLl9hY3RpdmVCdWZmZXIuZ2V0QmxhbmtMaW5lKHRoaXMuX2VyYXNlQXR0ckRhdGEoKSkpO3JldHVybiB0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya1JhbmdlRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSksdGhpcy5fYWN0aXZlQnVmZmVyLng9MCwhMH0sdC5wcm90b3R5cGUuZGVsZXRlTGluZXM9ZnVuY3Rpb24oZSl7dGhpcy5fcmVzdHJpY3RDdXJzb3IoKTt2YXIgdD1lLnBhcmFtc1swXXx8MTtpZih0aGlzLl9hY3RpdmVCdWZmZXIueT50aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tfHx0aGlzLl9hY3RpdmVCdWZmZXIueTx0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wKXJldHVybiEwO3ZhciByLGk9dGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55O2ZvcihyPXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xLXRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20scj10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSt0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2Utcjt0LS07KXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zcGxpY2UoaSwxKSx0aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuc3BsaWNlKHIsMCx0aGlzLl9hY3RpdmVCdWZmZXIuZ2V0QmxhbmtMaW5lKHRoaXMuX2VyYXNlQXR0ckRhdGEoKSkpO3JldHVybiB0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya1JhbmdlRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSksdGhpcy5fYWN0aXZlQnVmZmVyLng9MCwhMH0sdC5wcm90b3R5cGUuaW5zZXJ0Q2hhcnM9ZnVuY3Rpb24oZSl7dGhpcy5fcmVzdHJpY3RDdXJzb3IoKTt2YXIgdD10aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuZ2V0KHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZSt0aGlzLl9hY3RpdmVCdWZmZXIueSk7cmV0dXJuIHQmJih0Lmluc2VydENlbGxzKHRoaXMuX2FjdGl2ZUJ1ZmZlci54LGUucGFyYW1zWzBdfHwxLHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIueSkpLCEwfSx0LnByb3RvdHlwZS5kZWxldGVDaGFycz1mdW5jdGlvbihlKXt0aGlzLl9yZXN0cmljdEN1cnNvcigpO3ZhciB0PXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55KTtyZXR1cm4gdCYmKHQuZGVsZXRlQ2VsbHModGhpcy5fYWN0aXZlQnVmZmVyLngsZS5wYXJhbXNbMF18fDEsdGhpcy5fYWN0aXZlQnVmZmVyLmdldE51bGxDZWxsKHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksdGhpcy5fZXJhc2VBdHRyRGF0YSgpKSx0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci55KSksITB9LHQucHJvdG90eXBlLnNjcm9sbFVwPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MTt0LS07KXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zcGxpY2UodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3AsMSksdGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLnNwbGljZSh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSwwLHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXRCbGFua0xpbmUodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSk7cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5zY3JvbGxEb3duPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MTt0LS07KXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zcGxpY2UodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20sMSksdGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLnNwbGljZSh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcCwwLHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXRCbGFua0xpbmUoZi5ERUZBVUxUX0FUVFJfREFUQSkpO3JldHVybiB0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya1JhbmdlRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcCx0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tKSwhMH0sdC5wcm90b3R5cGUuc2Nyb2xsTGVmdD1mdW5jdGlvbihlKXtpZih0aGlzLl9hY3RpdmVCdWZmZXIueT50aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tfHx0aGlzLl9hY3RpdmVCdWZmZXIueTx0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wKXJldHVybiEwO2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MSxyPXRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3A7cjw9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbTsrK3Ipe3ZhciBpPXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3IpO2kuZGVsZXRlQ2VsbHMoMCx0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaS5pc1dyYXBwZWQ9ITF9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5zY3JvbGxSaWdodD1mdW5jdGlvbihlKXtpZih0aGlzLl9hY3RpdmVCdWZmZXIueT50aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tfHx0aGlzLl9hY3RpdmVCdWZmZXIueTx0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wKXJldHVybiEwO2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MSxyPXRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3A7cjw9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbTsrK3Ipe3ZhciBpPXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3IpO2kuaW5zZXJ0Q2VsbHMoMCx0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaS5pc1dyYXBwZWQ9ITF9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5pbnNlcnRDb2x1bW5zPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci55PnRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b218fHRoaXMuX2FjdGl2ZUJ1ZmZlci55PHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3ApcmV0dXJuITA7Zm9yKHZhciB0PWUucGFyYW1zWzBdfHwxLHI9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcDtyPD10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tOysrcil7dmFyIGk9dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2Urcik7aS5pbnNlcnRDZWxscyh0aGlzLl9hY3RpdmVCdWZmZXIueCx0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaS5pc1dyYXBwZWQ9ITF9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5kZWxldGVDb2x1bW5zPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci55PnRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b218fHRoaXMuX2FjdGl2ZUJ1ZmZlci55PHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3ApcmV0dXJuITA7Zm9yKHZhciB0PWUucGFyYW1zWzBdfHwxLHI9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcDtyPD10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tOysrcil7dmFyIGk9dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2Urcik7aS5kZWxldGVDZWxscyh0aGlzLl9hY3RpdmVCdWZmZXIueCx0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaS5pc1dyYXBwZWQ9ITF9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5lcmFzZUNoYXJzPWZ1bmN0aW9uKGUpe3RoaXMuX3Jlc3RyaWN0Q3Vyc29yKCk7dmFyIHQ9dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkpO3JldHVybiB0JiYodC5yZXBsYWNlQ2VsbHModGhpcy5fYWN0aXZlQnVmZmVyLngsdGhpcy5fYWN0aXZlQnVmZmVyLngrKGUucGFyYW1zWzBdfHwxKSx0aGlzLl9hY3RpdmVCdWZmZXIuZ2V0TnVsbENlbGwodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSx0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnkpKSwhMH0sdC5wcm90b3R5cGUucmVwZWF0UHJlY2VkaW5nQ2hhcmFjdGVyPWZ1bmN0aW9uKGUpe2lmKCF0aGlzLl9wYXJzZXIucHJlY2VkaW5nQ29kZXBvaW50KXJldHVybiEwO2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MSxyPW5ldyBVaW50MzJBcnJheSh0KSxpPTA7aTx0OysraSlyW2ldPXRoaXMuX3BhcnNlci5wcmVjZWRpbmdDb2RlcG9pbnQ7cmV0dXJuIHRoaXMucHJpbnQociwwLHIubGVuZ3RoKSwhMH0sdC5wcm90b3R5cGUuc2VuZERldmljZUF0dHJpYnV0ZXNQcmltYXJ5PWZ1bmN0aW9uKGUpe3JldHVybiBlLnBhcmFtc1swXT4wfHwodGhpcy5faXMoInh0ZXJtIil8fHRoaXMuX2lzKCJyeHZ0LXVuaWNvZGUiKXx8dGhpcy5faXMoInNjcmVlbiIpP3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIls/MTsyYyIpOnRoaXMuX2lzKCJsaW51eCIpJiZ0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKyJbPzZjIikpLCEwfSx0LnByb3RvdHlwZS5zZW5kRGV2aWNlQXR0cmlidXRlc1NlY29uZGFyeT1mdW5jdGlvbihlKXtyZXR1cm4gZS5wYXJhbXNbMF0+MHx8KHRoaXMuX2lzKCJ4dGVybSIpP3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIls+MDsyNzY7MGMiKTp0aGlzLl9pcygicnh2dC11bmljb2RlIik/dGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChzLkMwLkVTQysiWz44NTs5NTswYyIpOnRoaXMuX2lzKCJsaW51eCIpP3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoZS5wYXJhbXNbMF0rImMiKTp0aGlzLl9pcygic2NyZWVuIikmJnRoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIls+ODM7NDAwMDM7MGMiKSksITB9LHQucHJvdG90eXBlLl9pcz1mdW5jdGlvbihlKXtyZXR1cm4gMD09PSh0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLnRlcm1OYW1lKyIiKS5pbmRleE9mKGUpfSx0LnByb3RvdHlwZS5zZXRNb2RlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8ZS5sZW5ndGg7dCsrKTQ9PT1lLnBhcmFtc1t0XSYmKHRoaXMuX2NvcmVTZXJ2aWNlLm1vZGVzLmluc2VydE1vZGU9ITApO3JldHVybiEwfSx0LnByb3RvdHlwZS5zZXRNb2RlUHJpdmF0ZT1mdW5jdGlvbihlKXtmb3IodmFyIHQ9MDt0PGUubGVuZ3RoO3QrKylzd2l0Y2goZS5wYXJhbXNbdF0pe2Nhc2UgMTp0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuYXBwbGljYXRpb25DdXJzb3JLZXlzPSEwO2JyZWFrO2Nhc2UgMjp0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnQ2hhcnNldCgwLGEuREVGQVVMVF9DSEFSU0VUKSx0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnQ2hhcnNldCgxLGEuREVGQVVMVF9DSEFSU0VUKSx0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnQ2hhcnNldCgyLGEuREVGQVVMVF9DSEFSU0VUKSx0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnQ2hhcnNldCgzLGEuREVGQVVMVF9DSEFSU0VUKTticmVhaztjYXNlIDM6dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy53aW5kb3dPcHRpb25zLnNldFdpbkxpbmVzJiYodGhpcy5fYnVmZmVyU2VydmljZS5yZXNpemUoMTMyLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyksdGhpcy5fb25SZXF1ZXN0UmVzZXQuZmlyZSgpKTticmVhaztjYXNlIDY6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLm9yaWdpbj0hMCx0aGlzLl9zZXRDdXJzb3IoMCwwKTticmVhaztjYXNlIDc6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLndyYXBhcm91bmQ9ITA7YnJlYWs7Y2FzZSAxMjpicmVhaztjYXNlIDQ1OnRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5yZXZlcnNlV3JhcGFyb3VuZD0hMDticmVhaztjYXNlIDY2OnRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIlNlcmlhbCBwb3J0IHJlcXVlc3RlZCBhcHBsaWNhdGlvbiBrZXlwYWQuIiksdGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmFwcGxpY2F0aW9uS2V5cGFkPSEwLHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZmlyZSgpO2JyZWFrO2Nhc2UgOTp0aGlzLl9jb3JlTW91c2VTZXJ2aWNlLmFjdGl2ZVByb3RvY29sPSJYMTAiO2JyZWFrO2Nhc2UgMWUzOnRoaXMuX2NvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2w9IlZUMjAwIjticmVhaztjYXNlIDEwMDI6dGhpcy5fY29yZU1vdXNlU2VydmljZS5hY3RpdmVQcm90b2NvbD0iRFJBRyI7YnJlYWs7Y2FzZSAxMDAzOnRoaXMuX2NvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2w9IkFOWSI7YnJlYWs7Y2FzZSAxMDA0OnRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5zZW5kRm9jdXM9ITAsdGhpcy5fb25SZXF1ZXN0U2VuZEZvY3VzLmZpcmUoKTticmVhaztjYXNlIDEwMDU6dGhpcy5fbG9nU2VydmljZS5kZWJ1ZygiREVDU0VUIDEwMDUgbm90IHN1cHBvcnRlZCAoc2VlICMyNTA3KSIpO2JyZWFrO2Nhc2UgMTAwNjp0aGlzLl9jb3JlTW91c2VTZXJ2aWNlLmFjdGl2ZUVuY29kaW5nPSJTR1IiO2JyZWFrO2Nhc2UgMTAxNTp0aGlzLl9sb2dTZXJ2aWNlLmRlYnVnKCJERUNTRVQgMTAxNSBub3Qgc3VwcG9ydGVkIChzZWUgIzI1MDcpIik7YnJlYWs7Y2FzZSAyNTp0aGlzLl9jb3JlU2VydmljZS5pc0N1cnNvckhpZGRlbj0hMTticmVhaztjYXNlIDEwNDg6dGhpcy5zYXZlQ3Vyc29yKCk7YnJlYWs7Y2FzZSAxMDQ5OnRoaXMuc2F2ZUN1cnNvcigpO2Nhc2UgNDc6Y2FzZSAxMDQ3OnRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVycy5hY3RpdmF0ZUFsdEJ1ZmZlcih0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2NvcmVTZXJ2aWNlLmlzQ3Vyc29ySW5pdGlhbGl6ZWQ9ITAsdGhpcy5fb25SZXF1ZXN0UmVmcmVzaFJvd3MuZmlyZSgwLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xKSx0aGlzLl9vblJlcXVlc3RTeW5jU2Nyb2xsQmFyLmZpcmUoKTticmVhaztjYXNlIDIwMDQ6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmJyYWNrZXRlZFBhc3RlTW9kZT0hMH1yZXR1cm4hMH0sdC5wcm90b3R5cGUucmVzZXRNb2RlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8ZS5sZW5ndGg7dCsrKTQ9PT1lLnBhcmFtc1t0XSYmKHRoaXMuX2NvcmVTZXJ2aWNlLm1vZGVzLmluc2VydE1vZGU9ITEpO3JldHVybiEwfSx0LnByb3RvdHlwZS5yZXNldE1vZGVQcml2YXRlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8ZS5sZW5ndGg7dCsrKXN3aXRjaChlLnBhcmFtc1t0XSl7Y2FzZSAxOnRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5hcHBsaWNhdGlvbkN1cnNvcktleXM9ITE7YnJlYWs7Y2FzZSAzOnRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMud2luZG93T3B0aW9ucy5zZXRXaW5MaW5lcyYmKHRoaXMuX2J1ZmZlclNlcnZpY2UucmVzaXplKDgwLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyksdGhpcy5fb25SZXF1ZXN0UmVzZXQuZmlyZSgpKTticmVhaztjYXNlIDY6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLm9yaWdpbj0hMSx0aGlzLl9zZXRDdXJzb3IoMCwwKTticmVhaztjYXNlIDc6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLndyYXBhcm91bmQ9ITE7YnJlYWs7Y2FzZSAxMjpicmVhaztjYXNlIDQ1OnRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5yZXZlcnNlV3JhcGFyb3VuZD0hMTticmVhaztjYXNlIDY2OnRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIlN3aXRjaGluZyBiYWNrIHRvIG5vcm1hbCBrZXlwYWQuIiksdGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmFwcGxpY2F0aW9uS2V5cGFkPSExLHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZmlyZSgpO2JyZWFrO2Nhc2UgOTpjYXNlIDFlMzpjYXNlIDEwMDI6Y2FzZSAxMDAzOnRoaXMuX2NvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2w9Ik5PTkUiO2JyZWFrO2Nhc2UgMTAwNDp0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuc2VuZEZvY3VzPSExO2JyZWFrO2Nhc2UgMTAwNTp0aGlzLl9sb2dTZXJ2aWNlLmRlYnVnKCJERUNSU1QgMTAwNSBub3Qgc3VwcG9ydGVkIChzZWUgIzI1MDcpIik7YnJlYWs7Y2FzZSAxMDA2OnRoaXMuX2NvcmVNb3VzZVNlcnZpY2UuYWN0aXZlRW5jb2Rpbmc9IkRFRkFVTFQiO2JyZWFrO2Nhc2UgMTAxNTp0aGlzLl9sb2dTZXJ2aWNlLmRlYnVnKCJERUNSU1QgMTAxNSBub3Qgc3VwcG9ydGVkIChzZWUgIzI1MDcpIik7YnJlYWs7Y2FzZSAyNTp0aGlzLl9jb3JlU2VydmljZS5pc0N1cnNvckhpZGRlbj0hMDticmVhaztjYXNlIDEwNDg6dGhpcy5yZXN0b3JlQ3Vyc29yKCk7YnJlYWs7Y2FzZSAxMDQ5OmNhc2UgNDc6Y2FzZSAxMDQ3OnRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVycy5hY3RpdmF0ZU5vcm1hbEJ1ZmZlcigpLDEwNDk9PT1lLnBhcmFtc1t0XSYmdGhpcy5yZXN0b3JlQ3Vyc29yKCksdGhpcy5fY29yZVNlcnZpY2UuaXNDdXJzb3JJbml0aWFsaXplZD0hMCx0aGlzLl9vblJlcXVlc3RSZWZyZXNoUm93cy5maXJlKDAsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEpLHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZmlyZSgpO2JyZWFrO2Nhc2UgMjAwNDp0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuYnJhY2tldGVkUGFzdGVNb2RlPSExfXJldHVybiEwfSx0LnByb3RvdHlwZS5fdXBkYXRlQXR0ckNvbG9yPWZ1bmN0aW9uKGUsdCxyLGksbil7cmV0dXJuIDI9PT10PyhlfD01MDMzMTY0OCxlJj0tMTY3NzcyMTYsZXw9di5BdHRyaWJ1dGVEYXRhLmZyb21Db2xvclJHQihbcixpLG5dKSk6NT09PXQmJihlJj0tNTAzMzE5MDQsZXw9MzM1NTQ0MzJ8MjU1JnIpLGV9LHQucHJvdG90eXBlLl9leHRyYWN0Q29sb3I9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPVswLDAsLTEsMCwwLDBdLG49MCxvPTA7ZG97aWYoaVtvK25dPWUucGFyYW1zW3Qrb10sZS5oYXNTdWJQYXJhbXModCtvKSl7dmFyIHM9ZS5nZXRTdWJQYXJhbXModCtvKSxhPTA7ZG97NT09PWlbMV0mJihuPTEpLGlbbythKzErbl09c1thXX13aGlsZSgrK2E8cy5sZW5ndGgmJmErbysxK248aS5sZW5ndGgpO2JyZWFrfWlmKDU9PT1pWzFdJiZvK24+PTJ8fDI9PT1pWzFdJiZvK24+PTUpYnJlYWs7aVsxXSYmKG49MSl9d2hpbGUoKytvK3Q8ZS5sZW5ndGgmJm8rbjxpLmxlbmd0aCk7Zm9yKGE9MjthPGkubGVuZ3RoOysrYSktMT09PWlbYV0mJihpW2FdPTApO3N3aXRjaChpWzBdKXtjYXNlIDM4OnIuZmc9dGhpcy5fdXBkYXRlQXR0ckNvbG9yKHIuZmcsaVsxXSxpWzNdLGlbNF0saVs1XSk7YnJlYWs7Y2FzZSA0ODpyLmJnPXRoaXMuX3VwZGF0ZUF0dHJDb2xvcihyLmJnLGlbMV0saVszXSxpWzRdLGlbNV0pO2JyZWFrO2Nhc2UgNTg6ci5leHRlbmRlZD1yLmV4dGVuZGVkLmNsb25lKCksci5leHRlbmRlZC51bmRlcmxpbmVDb2xvcj10aGlzLl91cGRhdGVBdHRyQ29sb3Ioci5leHRlbmRlZC51bmRlcmxpbmVDb2xvcixpWzFdLGlbM10saVs0XSxpWzVdKX1yZXR1cm4gb30sdC5wcm90b3R5cGUuX3Byb2Nlc3NVbmRlcmxpbmU9ZnVuY3Rpb24oZSx0KXt0LmV4dGVuZGVkPXQuZXh0ZW5kZWQuY2xvbmUoKSwoIX5lfHxlPjUpJiYoZT0xKSx0LmV4dGVuZGVkLnVuZGVybGluZVN0eWxlPWUsdC5mZ3w9MjY4NDM1NDU2LDA9PT1lJiYodC5mZyY9LTI2ODQzNTQ1NyksdC51cGRhdGVFeHRlbmRlZCgpfSx0LnByb3RvdHlwZS5jaGFyQXR0cmlidXRlcz1mdW5jdGlvbihlKXtpZigxPT09ZS5sZW5ndGgmJjA9PT1lLnBhcmFtc1swXSlyZXR1cm4gdGhpcy5fY3VyQXR0ckRhdGEuZmc9Zi5ERUZBVUxUX0FUVFJfREFUQS5mZyx0aGlzLl9jdXJBdHRyRGF0YS5iZz1mLkRFRkFVTFRfQVRUUl9EQVRBLmJnLCEwO2Zvcih2YXIgdCxyPWUubGVuZ3RoLGk9dGhpcy5fY3VyQXR0ckRhdGEsbj0wO248cjtuKyspKHQ9ZS5wYXJhbXNbbl0pPj0zMCYmdDw9Mzc/KGkuZmcmPS01MDMzMTkwNCxpLmZnfD0xNjc3NzIxNnx0LTMwKTp0Pj00MCYmdDw9NDc/KGkuYmcmPS01MDMzMTkwNCxpLmJnfD0xNjc3NzIxNnx0LTQwKTp0Pj05MCYmdDw9OTc/KGkuZmcmPS01MDMzMTkwNCxpLmZnfD0xNjc3NzIyNHx0LTkwKTp0Pj0xMDAmJnQ8PTEwNz8oaS5iZyY9LTUwMzMxOTA0LGkuYmd8PTE2Nzc3MjI0fHQtMTAwKTowPT09dD8oaS5mZz1mLkRFRkFVTFRfQVRUUl9EQVRBLmZnLGkuYmc9Zi5ERUZBVUxUX0FUVFJfREFUQS5iZyk6MT09PXQ/aS5mZ3w9MTM0MjE3NzI4OjM9PT10P2kuYmd8PTY3MTA4ODY0OjQ9PT10PyhpLmZnfD0yNjg0MzU0NTYsdGhpcy5fcHJvY2Vzc1VuZGVybGluZShlLmhhc1N1YlBhcmFtcyhuKT9lLmdldFN1YlBhcmFtcyhuKVswXToxLGkpKTo1PT09dD9pLmZnfD01MzY4NzA5MTI6Nz09PXQ/aS5mZ3w9NjcxMDg4NjQ6OD09PXQ/aS5mZ3w9MTA3Mzc0MTgyNDo5PT09dD9pLmZnfD0yMTQ3NDgzNjQ4OjI9PT10P2kuYmd8PTEzNDIxNzcyODoyMT09PXQ/dGhpcy5fcHJvY2Vzc1VuZGVybGluZSgyLGkpOjIyPT09dD8oaS5mZyY9LTEzNDIxNzcyOSxpLmJnJj0tMTM0MjE3NzI5KToyMz09PXQ/aS5iZyY9LTY3MTA4ODY1OjI0PT09dD9pLmZnJj0tMjY4NDM1NDU3OjI1PT09dD9pLmZnJj0tNTM2ODcwOTEzOjI3PT09dD9pLmZnJj0tNjcxMDg4NjU6Mjg9PT10P2kuZmcmPS0xMDczNzQxODI1OjI5PT09dD9pLmZnJj0yMTQ3NDgzNjQ3OjM5PT09dD8oaS5mZyY9LTY3MTA4ODY0LGkuZmd8PTE2Nzc3MjE1JmYuREVGQVVMVF9BVFRSX0RBVEEuZmcpOjQ5PT09dD8oaS5iZyY9LTY3MTA4ODY0LGkuYmd8PTE2Nzc3MjE1JmYuREVGQVVMVF9BVFRSX0RBVEEuYmcpOjM4PT09dHx8NDg9PT10fHw1OD09PXQ/bis9dGhpcy5fZXh0cmFjdENvbG9yKGUsbixpKTo1OT09PXQ/KGkuZXh0ZW5kZWQ9aS5leHRlbmRlZC5jbG9uZSgpLGkuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3I9LTEsaS51cGRhdGVFeHRlbmRlZCgpKToxMDA9PT10PyhpLmZnJj0tNjcxMDg4NjQsaS5mZ3w9MTY3NzcyMTUmZi5ERUZBVUxUX0FUVFJfREFUQS5mZyxpLmJnJj0tNjcxMDg4NjQsaS5iZ3w9MTY3NzcyMTUmZi5ERUZBVUxUX0FUVFJfREFUQS5iZyk6dGhpcy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBTR1IgYXR0cmlidXRlOiAlZC4iLHQpO3JldHVybiEwfSx0LnByb3RvdHlwZS5kZXZpY2VTdGF0dXM9ZnVuY3Rpb24oZSl7c3dpdGNoKGUucGFyYW1zWzBdKXtjYXNlIDU6dGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChzLkMwLkVTQysiWzBuIik7YnJlYWs7Y2FzZSA2OnZhciB0PXRoaXMuX2FjdGl2ZUJ1ZmZlci55KzEscj10aGlzLl9hY3RpdmVCdWZmZXIueCsxO3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIlsiK3QrIjsiK3IrIlIiKX1yZXR1cm4hMH0sdC5wcm90b3R5cGUuZGV2aWNlU3RhdHVzUHJpdmF0ZT1mdW5jdGlvbihlKXtpZig2PT09ZS5wYXJhbXNbMF0pe3ZhciB0PXRoaXMuX2FjdGl2ZUJ1ZmZlci55KzEscj10aGlzLl9hY3RpdmVCdWZmZXIueCsxO3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIls/Iit0KyI7IityKyJSIil9cmV0dXJuITB9LHQucHJvdG90eXBlLnNvZnRSZXNldD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fY29yZVNlcnZpY2UuaXNDdXJzb3JIaWRkZW49ITEsdGhpcy5fb25SZXF1ZXN0U3luY1Njcm9sbEJhci5maXJlKCksdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcD0wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b209dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEsdGhpcy5fY3VyQXR0ckRhdGE9Zi5ERUZBVUxUX0FUVFJfREFUQS5jbG9uZSgpLHRoaXMuX2NvcmVTZXJ2aWNlLnJlc2V0KCksdGhpcy5fY2hhcnNldFNlcnZpY2UucmVzZXQoKSx0aGlzLl9hY3RpdmVCdWZmZXIuc2F2ZWRYPTAsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkWT10aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ3VyQXR0ckRhdGEuZmc9dGhpcy5fY3VyQXR0ckRhdGEuZmcsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ3VyQXR0ckRhdGEuYmc9dGhpcy5fY3VyQXR0ckRhdGEuYmcsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ2hhcnNldD10aGlzLl9jaGFyc2V0U2VydmljZS5jaGFyc2V0LHRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5vcmlnaW49ITEsITB9LHQucHJvdG90eXBlLnNldEN1cnNvclN0eWxlPWZ1bmN0aW9uKGUpe3ZhciB0PWUucGFyYW1zWzBdfHwxO3N3aXRjaCh0KXtjYXNlIDE6Y2FzZSAyOnRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGU9ImJsb2NrIjticmVhaztjYXNlIDM6Y2FzZSA0OnRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGU9InVuZGVybGluZSI7YnJlYWs7Y2FzZSA1OmNhc2UgNjp0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmN1cnNvclN0eWxlPSJiYXIifXZhciByPXQlMj09MTtyZXR1cm4gdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JCbGluaz1yLCEwfSx0LnByb3RvdHlwZS5zZXRTY3JvbGxSZWdpb249ZnVuY3Rpb24oZSl7dmFyIHQscj1lLnBhcmFtc1swXXx8MTtyZXR1cm4oZS5sZW5ndGg8Mnx8KHQ9ZS5wYXJhbXNbMV0pPnRoaXMuX2J1ZmZlclNlcnZpY2Uucm93c3x8MD09PXQpJiYodD10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MpLHQ+ciYmKHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3A9ci0xLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b209dC0xLHRoaXMuX3NldEN1cnNvcigwLDApKSwhMH0sdC5wcm90b3R5cGUud2luZG93T3B0aW9ucz1mdW5jdGlvbihlKXtpZighdyhlLnBhcmFtc1swXSx0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLndpbmRvd09wdGlvbnMpKXJldHVybiEwO3ZhciB0PWUubGVuZ3RoPjE/ZS5wYXJhbXNbMV06MDtzd2l0Y2goZS5wYXJhbXNbMF0pe2Nhc2UgMTQ6MiE9PXQmJnRoaXMuX29uUmVxdWVzdFdpbmRvd3NPcHRpb25zUmVwb3J0LmZpcmUoby5HRVRfV0lOX1NJWkVfUElYRUxTKTticmVhaztjYXNlIDE2OnRoaXMuX29uUmVxdWVzdFdpbmRvd3NPcHRpb25zUmVwb3J0LmZpcmUoby5HRVRfQ0VMTF9TSVpFX1BJWEVMUyk7YnJlYWs7Y2FzZSAxODp0aGlzLl9idWZmZXJTZXJ2aWNlJiZ0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKyJbODsiK3RoaXMuX2J1ZmZlclNlcnZpY2Uucm93cysiOyIrdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKyJ0Iik7YnJlYWs7Y2FzZSAyMjowIT09dCYmMiE9PXR8fCh0aGlzLl93aW5kb3dUaXRsZVN0YWNrLnB1c2godGhpcy5fd2luZG93VGl0bGUpLHRoaXMuX3dpbmRvd1RpdGxlU3RhY2subGVuZ3RoPjEwJiZ0aGlzLl93aW5kb3dUaXRsZVN0YWNrLnNoaWZ0KCkpLDAhPT10JiYxIT09dHx8KHRoaXMuX2ljb25OYW1lU3RhY2sucHVzaCh0aGlzLl9pY29uTmFtZSksdGhpcy5faWNvbk5hbWVTdGFjay5sZW5ndGg+MTAmJnRoaXMuX2ljb25OYW1lU3RhY2suc2hpZnQoKSk7YnJlYWs7Y2FzZSAyMzowIT09dCYmMiE9PXR8fHRoaXMuX3dpbmRvd1RpdGxlU3RhY2subGVuZ3RoJiZ0aGlzLnNldFRpdGxlKHRoaXMuX3dpbmRvd1RpdGxlU3RhY2sucG9wKCkpLDAhPT10JiYxIT09dHx8dGhpcy5faWNvbk5hbWVTdGFjay5sZW5ndGgmJnRoaXMuc2V0SWNvbk5hbWUodGhpcy5faWNvbk5hbWVTdGFjay5wb3AoKSl9cmV0dXJuITB9LHQucHJvdG90eXBlLnNhdmVDdXJzb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZFg9dGhpcy5fYWN0aXZlQnVmZmVyLngsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkWT10aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ3VyQXR0ckRhdGEuZmc9dGhpcy5fY3VyQXR0ckRhdGEuZmcsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ3VyQXR0ckRhdGEuYmc9dGhpcy5fY3VyQXR0ckRhdGEuYmcsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ2hhcnNldD10aGlzLl9jaGFyc2V0U2VydmljZS5jaGFyc2V0LCEwfSx0LnByb3RvdHlwZS5yZXN0b3JlQ3Vyc29yPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9hY3RpdmVCdWZmZXIueD10aGlzLl9hY3RpdmVCdWZmZXIuc2F2ZWRYfHwwLHRoaXMuX2FjdGl2ZUJ1ZmZlci55PU1hdGgubWF4KHRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZFktdGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlLDApLHRoaXMuX2N1ckF0dHJEYXRhLmZnPXRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZEN1ckF0dHJEYXRhLmZnLHRoaXMuX2N1ckF0dHJEYXRhLmJnPXRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZEN1ckF0dHJEYXRhLmJnLHRoaXMuX2NoYXJzZXRTZXJ2aWNlLmNoYXJzZXQ9dGhpcy5fc2F2ZWRDaGFyc2V0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZENoYXJzZXQmJih0aGlzLl9jaGFyc2V0U2VydmljZS5jaGFyc2V0PXRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZENoYXJzZXQpLHRoaXMuX3Jlc3RyaWN0Q3Vyc29yKCksITB9LHQucHJvdG90eXBlLnNldFRpdGxlPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl93aW5kb3dUaXRsZT1lLHRoaXMuX29uVGl0bGVDaGFuZ2UuZmlyZShlKSwhMH0sdC5wcm90b3R5cGUuc2V0SWNvbk5hbWU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2ljb25OYW1lPWUsITB9LHQucHJvdG90eXBlLnNldE9yUmVwb3J0SW5kZXhlZENvbG9yPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1bXSxyPWUuc3BsaXQoIjsiKTtyLmxlbmd0aD4xOyl7dmFyIGk9ci5zaGlmdCgpLG49ci5zaGlmdCgpO2lmKC9eXGQrJC8uZXhlYyhpKSl7dmFyIG89cGFyc2VJbnQoaSk7aWYoMDw9byYmbzwyNTYpaWYoIj8iPT09bil0LnB1c2goe3R5cGU6MCxpbmRleDpvfSk7ZWxzZXt2YXIgcz0oMCxiLnBhcnNlQ29sb3IpKG4pO3MmJnQucHVzaCh7dHlwZToxLGluZGV4Om8sY29sb3I6c30pfX19cmV0dXJuIHQubGVuZ3RoJiZ0aGlzLl9vbkNvbG9yLmZpcmUodCksITB9LHQucHJvdG90eXBlLl9zZXRPclJlcG9ydFNwZWNpYWxDb2xvcj1mdW5jdGlvbihlLHQpe2Zvcih2YXIgcj1lLnNwbGl0KCI7IiksaT0wO2k8ci5sZW5ndGgmJiEodD49dGhpcy5fc3BlY2lhbENvbG9ycy5sZW5ndGgpOysraSwrK3QpaWYoIj8iPT09cltpXSl0aGlzLl9vbkNvbG9yLmZpcmUoW3t0eXBlOjAsaW5kZXg6dGhpcy5fc3BlY2lhbENvbG9yc1t0XX1dKTtlbHNle3ZhciBuPSgwLGIucGFyc2VDb2xvcikocltpXSk7biYmdGhpcy5fb25Db2xvci5maXJlKFt7dHlwZToxLGluZGV4OnRoaXMuX3NwZWNpYWxDb2xvcnNbdF0sY29sb3I6bn1dKX1yZXR1cm4hMH0sdC5wcm90b3R5cGUuc2V0T3JSZXBvcnRGZ0NvbG9yPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9zZXRPclJlcG9ydFNwZWNpYWxDb2xvcihlLDApfSx0LnByb3RvdHlwZS5zZXRPclJlcG9ydEJnQ29sb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX3NldE9yUmVwb3J0U3BlY2lhbENvbG9yKGUsMSl9LHQucHJvdG90eXBlLnNldE9yUmVwb3J0Q3Vyc29yQ29sb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX3NldE9yUmVwb3J0U3BlY2lhbENvbG9yKGUsMil9LHQucHJvdG90eXBlLnJlc3RvcmVJbmRleGVkQ29sb3I9ZnVuY3Rpb24oZSl7aWYoIWUpcmV0dXJuIHRoaXMuX29uQ29sb3IuZmlyZShbe3R5cGU6Mn1dKSwhMDtmb3IodmFyIHQ9W10scj1lLnNwbGl0KCI7IiksaT0wO2k8ci5sZW5ndGg7KytpKWlmKC9eXGQrJC8uZXhlYyhyW2ldKSl7dmFyIG49cGFyc2VJbnQocltpXSk7MDw9biYmbjwyNTYmJnQucHVzaCh7dHlwZToyLGluZGV4Om59KX1yZXR1cm4gdC5sZW5ndGgmJnRoaXMuX29uQ29sb3IuZmlyZSh0KSwhMH0sdC5wcm90b3R5cGUucmVzdG9yZUZnQ29sb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX29uQ29sb3IuZmlyZShbe3R5cGU6MixpbmRleDoyNTZ9XSksITB9LHQucHJvdG90eXBlLnJlc3RvcmVCZ0NvbG9yPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9vbkNvbG9yLmZpcmUoW3t0eXBlOjIsaW5kZXg6MjU3fV0pLCEwfSx0LnByb3RvdHlwZS5yZXN0b3JlQ3Vyc29yQ29sb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX29uQ29sb3IuZmlyZShbe3R5cGU6MixpbmRleDoyNTh9XSksITB9LHQucHJvdG90eXBlLm5leHRMaW5lPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2FjdGl2ZUJ1ZmZlci54PTAsdGhpcy5pbmRleCgpLCEwfSx0LnByb3RvdHlwZS5rZXlwYWRBcHBsaWNhdGlvbk1vZGU9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fbG9nU2VydmljZS5kZWJ1ZygiU2VyaWFsIHBvcnQgcmVxdWVzdGVkIGFwcGxpY2F0aW9uIGtleXBhZC4iKSx0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuYXBwbGljYXRpb25LZXlwYWQ9ITAsdGhpcy5fb25SZXF1ZXN0U3luY1Njcm9sbEJhci5maXJlKCksITB9LHQucHJvdG90eXBlLmtleXBhZE51bWVyaWNNb2RlPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIlN3aXRjaGluZyBiYWNrIHRvIG5vcm1hbCBrZXlwYWQuIiksdGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmFwcGxpY2F0aW9uS2V5cGFkPSExLHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZmlyZSgpLCEwfSx0LnByb3RvdHlwZS5zZWxlY3REZWZhdWx0Q2hhcnNldD1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnTGV2ZWwoMCksdGhpcy5fY2hhcnNldFNlcnZpY2Uuc2V0Z0NoYXJzZXQoMCxhLkRFRkFVTFRfQ0hBUlNFVCksITB9LHQucHJvdG90eXBlLnNlbGVjdENoYXJzZXQ9ZnVuY3Rpb24oZSl7cmV0dXJuIDIhPT1lLmxlbmd0aD8odGhpcy5zZWxlY3REZWZhdWx0Q2hhcnNldCgpLCEwKTooIi8iPT09ZVswXXx8dGhpcy5fY2hhcnNldFNlcnZpY2Uuc2V0Z0NoYXJzZXQoU1tlWzBdXSxhLkNIQVJTRVRTW2VbMV1dfHxhLkRFRkFVTFRfQ0hBUlNFVCksITApfSx0LnByb3RvdHlwZS5pbmRleD1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9yZXN0cmljdEN1cnNvcigpLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KyssdGhpcy5fYWN0aXZlQnVmZmVyLnk9PT10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tKzE/KHRoaXMuX2FjdGl2ZUJ1ZmZlci55LS0sdGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGwodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSk6dGhpcy5fYWN0aXZlQnVmZmVyLnk+PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyYmKHRoaXMuX2FjdGl2ZUJ1ZmZlci55PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xKSx0aGlzLl9yZXN0cmljdEN1cnNvcigpLCEwfSx0LnByb3RvdHlwZS50YWJTZXQ9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYWN0aXZlQnVmZmVyLnRhYnNbdGhpcy5fYWN0aXZlQnVmZmVyLnhdPSEwLCEwfSx0LnByb3RvdHlwZS5yZXZlcnNlSW5kZXg9ZnVuY3Rpb24oKXtpZih0aGlzLl9yZXN0cmljdEN1cnNvcigpLHRoaXMuX2FjdGl2ZUJ1ZmZlci55PT09dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcCl7dmFyIGU9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbS10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wO3RoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zaGlmdEVsZW1lbnRzKHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZSt0aGlzLl9hY3RpdmVCdWZmZXIueSxlLDEpLHRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXRCbGFua0xpbmUodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSksdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtSYW5nZURpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3AsdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSl9ZWxzZSB0aGlzLl9hY3RpdmVCdWZmZXIueS0tLHRoaXMuX3Jlc3RyaWN0Q3Vyc29yKCk7cmV0dXJuITB9LHQucHJvdG90eXBlLmZ1bGxSZXNldD1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9wYXJzZXIucmVzZXQoKSx0aGlzLl9vblJlcXVlc3RSZXNldC5maXJlKCksITB9LHQucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5fY3VyQXR0ckRhdGE9Zi5ERUZBVUxUX0FUVFJfREFUQS5jbG9uZSgpLHRoaXMuX2VyYXNlQXR0ckRhdGFJbnRlcm5hbD1mLkRFRkFVTFRfQVRUUl9EQVRBLmNsb25lKCl9LHQucHJvdG90eXBlLl9lcmFzZUF0dHJEYXRhPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2VyYXNlQXR0ckRhdGFJbnRlcm5hbC5iZyY9LTY3MTA4ODY0LHRoaXMuX2VyYXNlQXR0ckRhdGFJbnRlcm5hbC5iZ3w9NjcxMDg4NjMmdGhpcy5fY3VyQXR0ckRhdGEuYmcsdGhpcy5fZXJhc2VBdHRyRGF0YUludGVybmFsfSx0LnByb3RvdHlwZS5zZXRnTGV2ZWw9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2NoYXJzZXRTZXJ2aWNlLnNldGdMZXZlbChlKSwhMH0sdC5wcm90b3R5cGUuc2NyZWVuQWxpZ25tZW50UGF0dGVybj1mdW5jdGlvbigpe3ZhciBlPW5ldyBwLkNlbGxEYXRhO2UuY29udGVudD0xPDwyMnwiRSIuY2hhckNvZGVBdCgwKSxlLmZnPXRoaXMuX2N1ckF0dHJEYXRhLmZnLGUuYmc9dGhpcy5fY3VyQXR0ckRhdGEuYmcsdGhpcy5fc2V0Q3Vyc29yKDAsMCk7Zm9yKHZhciB0PTA7dDx0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3M7Kyt0KXt2YXIgcj10aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkrdCxpPXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQocik7aSYmKGkuZmlsbChlKSxpLmlzV3JhcHBlZD0hMSl9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrQWxsRGlydHkoKSx0aGlzLl9zZXRDdXJzb3IoMCwwKSwhMH0sdH0obC5EaXNwb3NhYmxlKTt0LklucHV0SGFuZGxlcj1FfSw4NDQ6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5nZXREaXNwb3NlQXJyYXlEaXNwb3NhYmxlPXQuZGlzcG9zZUFycmF5PXQuRGlzcG9zYWJsZT12b2lkIDA7dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKCl7dGhpcy5fZGlzcG9zYWJsZXM9W10sdGhpcy5faXNEaXNwb3NlZD0hMX1yZXR1cm4gZS5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3RoaXMuX2lzRGlzcG9zZWQ9ITA7Zm9yKHZhciBlPTAsdD10aGlzLl9kaXNwb3NhYmxlcztlPHQubGVuZ3RoO2UrKyl0W2VdLmRpc3Bvc2UoKTt0aGlzLl9kaXNwb3NhYmxlcy5sZW5ndGg9MH0sZS5wcm90b3R5cGUucmVnaXN0ZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2Rpc3Bvc2FibGVzLnB1c2goZSksZX0sZS5wcm90b3R5cGUudW5yZWdpc3Rlcj1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9kaXNwb3NhYmxlcy5pbmRleE9mKGUpOy0xIT09dCYmdGhpcy5fZGlzcG9zYWJsZXMuc3BsaWNlKHQsMSl9LGV9KCk7ZnVuY3Rpb24gaShlKXtmb3IodmFyIHQ9MCxyPWU7dDxyLmxlbmd0aDt0Kyspclt0XS5kaXNwb3NlKCk7ZS5sZW5ndGg9MH10LkRpc3Bvc2FibGU9cix0LmRpc3Bvc2VBcnJheT1pLHQuZ2V0RGlzcG9zZUFycmF5RGlzcG9zYWJsZT1mdW5jdGlvbihlKXtyZXR1cm57ZGlzcG9zZTpmdW5jdGlvbigpe3JldHVybiBpKGUpfX19fSw2MTE0OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuaXNMaW51eD10LmlzV2luZG93cz10LmlzSXBob25lPXQuaXNJcGFkPXQuaXNNYWM9dC5pc1NhZmFyaT10LmlzRmlyZWZveD12b2lkIDA7dmFyIHI9InVuZGVmaW5lZCI9PXR5cGVvZiBuYXZpZ2F0b3IsaT1yPyJub2RlIjpuYXZpZ2F0b3IudXNlckFnZW50LG49cj8ibm9kZSI6bmF2aWdhdG9yLnBsYXRmb3JtO3QuaXNGaXJlZm94PWkuaW5jbHVkZXMoIkZpcmVmb3giKSx0LmlzU2FmYXJpPS9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QoaSksdC5pc01hYz1bIk1hY2ludG9zaCIsIk1hY0ludGVsIiwiTWFjUFBDIiwiTWFjNjhLIl0uaW5jbHVkZXMobiksdC5pc0lwYWQ9ImlQYWQiPT09bix0LmlzSXBob25lPSJpUGhvbmUiPT09bix0LmlzV2luZG93cz1bIldpbmRvd3MiLCJXaW4xNiIsIldpbjMyIiwiV2luQ0UiXS5pbmNsdWRlcyhuKSx0LmlzTGludXg9bi5pbmRleE9mKCJMaW51eCIpPj0wfSw4MjczOihlLHQpPT57ZnVuY3Rpb24gcihlLHQscixpKXtpZih2b2lkIDA9PT1yJiYocj0wKSx2b2lkIDA9PT1pJiYoaT1lLmxlbmd0aCkscj49ZS5sZW5ndGgpcmV0dXJuIGU7cj0oZS5sZW5ndGgrciklZS5sZW5ndGgsaT1pPj1lLmxlbmd0aD9lLmxlbmd0aDooZS5sZW5ndGgraSklZS5sZW5ndGg7Zm9yKHZhciBuPXI7bjxpOysrbillW25dPXQ7cmV0dXJuIGV9T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuY29uY2F0PXQuZmlsbEZhbGxiYWNrPXQuZmlsbD12b2lkIDAsdC5maWxsPWZ1bmN0aW9uKGUsdCxpLG4pe3JldHVybiBlLmZpbGw/ZS5maWxsKHQsaSxuKTpyKGUsdCxpLG4pfSx0LmZpbGxGYWxsYmFjaz1yLHQuY29uY2F0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9bmV3IGUuY29uc3RydWN0b3IoZS5sZW5ndGgrdC5sZW5ndGgpO3JldHVybiByLnNldChlKSxyLnNldCh0LGUubGVuZ3RoKSxyfX0sOTI4MjooZSx0LHIpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQudXBkYXRlV2luZG93c01vZGVXcmFwcGVkU3RhdGU9dm9pZCAwO3ZhciBpPXIoNjQzKTt0LnVwZGF0ZVdpbmRvd3NNb2RlV3JhcHBlZFN0YXRlPWZ1bmN0aW9uKGUpe3ZhciB0PWUuYnVmZmVyLmxpbmVzLmdldChlLmJ1ZmZlci55YmFzZStlLmJ1ZmZlci55LTEpLHI9bnVsbD09dD92b2lkIDA6dC5nZXQoZS5jb2xzLTEpLG49ZS5idWZmZXIubGluZXMuZ2V0KGUuYnVmZmVyLnliYXNlK2UuYnVmZmVyLnkpO24mJnImJihuLmlzV3JhcHBlZD1yW2kuQ0hBUl9EQVRBX0NPREVfSU5ERVhdIT09aS5OVUxMX0NFTExfQ09ERSYmcltpLkNIQVJfREFUQV9DT0RFX0lOREVYXSE9PWkuV0hJVEVTUEFDRV9DRUxMX0NPREUpfX0sMzczNDooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkV4dGVuZGVkQXR0cnM9dC5BdHRyaWJ1dGVEYXRhPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt0aGlzLmZnPTAsdGhpcy5iZz0wLHRoaXMuZXh0ZW5kZWQ9bmV3IGl9cmV0dXJuIGUudG9Db2xvclJHQj1mdW5jdGlvbihlKXtyZXR1cm5bZT4+PjE2JjI1NSxlPj4+OCYyNTUsMjU1JmVdfSxlLmZyb21Db2xvclJHQj1mdW5jdGlvbihlKXtyZXR1cm4oMjU1JmVbMF0pPDwxNnwoMjU1JmVbMV0pPDw4fDI1NSZlWzJdfSxlLnByb3RvdHlwZS5jbG9uZT1mdW5jdGlvbigpe3ZhciB0PW5ldyBlO3JldHVybiB0LmZnPXRoaXMuZmcsdC5iZz10aGlzLmJnLHQuZXh0ZW5kZWQ9dGhpcy5leHRlbmRlZC5jbG9uZSgpLHR9LGUucHJvdG90eXBlLmlzSW52ZXJzZT1mdW5jdGlvbigpe3JldHVybiA2NzEwODg2NCZ0aGlzLmZnfSxlLnByb3RvdHlwZS5pc0JvbGQ9ZnVuY3Rpb24oKXtyZXR1cm4gMTM0MjE3NzI4JnRoaXMuZmd9LGUucHJvdG90eXBlLmlzVW5kZXJsaW5lPWZ1bmN0aW9uKCl7cmV0dXJuIDI2ODQzNTQ1NiZ0aGlzLmZnfSxlLnByb3RvdHlwZS5pc0JsaW5rPWZ1bmN0aW9uKCl7cmV0dXJuIDUzNjg3MDkxMiZ0aGlzLmZnfSxlLnByb3RvdHlwZS5pc0ludmlzaWJsZT1mdW5jdGlvbigpe3JldHVybiAxMDczNzQxODI0JnRoaXMuZmd9LGUucHJvdG90eXBlLmlzSXRhbGljPWZ1bmN0aW9uKCl7cmV0dXJuIDY3MTA4ODY0JnRoaXMuYmd9LGUucHJvdG90eXBlLmlzRGltPWZ1bmN0aW9uKCl7cmV0dXJuIDEzNDIxNzcyOCZ0aGlzLmJnfSxlLnByb3RvdHlwZS5pc1N0cmlrZXRocm91Z2g9ZnVuY3Rpb24oKXtyZXR1cm4gMjE0NzQ4MzY0OCZ0aGlzLmZnfSxlLnByb3RvdHlwZS5nZXRGZ0NvbG9yTW9kZT1mdW5jdGlvbigpe3JldHVybiA1MDMzMTY0OCZ0aGlzLmZnfSxlLnByb3RvdHlwZS5nZXRCZ0NvbG9yTW9kZT1mdW5jdGlvbigpe3JldHVybiA1MDMzMTY0OCZ0aGlzLmJnfSxlLnByb3RvdHlwZS5pc0ZnUkdCPWZ1bmN0aW9uKCl7cmV0dXJuIDUwMzMxNjQ4PT0oNTAzMzE2NDgmdGhpcy5mZyl9LGUucHJvdG90eXBlLmlzQmdSR0I9ZnVuY3Rpb24oKXtyZXR1cm4gNTAzMzE2NDg9PSg1MDMzMTY0OCZ0aGlzLmJnKX0sZS5wcm90b3R5cGUuaXNGZ1BhbGV0dGU9ZnVuY3Rpb24oKXtyZXR1cm4gMTY3NzcyMTY9PSg1MDMzMTY0OCZ0aGlzLmZnKXx8MzM1NTQ0MzI9PSg1MDMzMTY0OCZ0aGlzLmZnKX0sZS5wcm90b3R5cGUuaXNCZ1BhbGV0dGU9ZnVuY3Rpb24oKXtyZXR1cm4gMTY3NzcyMTY9PSg1MDMzMTY0OCZ0aGlzLmJnKXx8MzM1NTQ0MzI9PSg1MDMzMTY0OCZ0aGlzLmJnKX0sZS5wcm90b3R5cGUuaXNGZ0RlZmF1bHQ9ZnVuY3Rpb24oKXtyZXR1cm4gMD09KDUwMzMxNjQ4JnRoaXMuZmcpfSxlLnByb3RvdHlwZS5pc0JnRGVmYXVsdD1mdW5jdGlvbigpe3JldHVybiAwPT0oNTAzMzE2NDgmdGhpcy5iZyl9LGUucHJvdG90eXBlLmlzQXR0cmlidXRlRGVmYXVsdD1mdW5jdGlvbigpe3JldHVybiAwPT09dGhpcy5mZyYmMD09PXRoaXMuYmd9LGUucHJvdG90eXBlLmdldEZnQ29sb3I9ZnVuY3Rpb24oKXtzd2l0Y2goNTAzMzE2NDgmdGhpcy5mZyl7Y2FzZSAxNjc3NzIxNjpjYXNlIDMzNTU0NDMyOnJldHVybiAyNTUmdGhpcy5mZztjYXNlIDUwMzMxNjQ4OnJldHVybiAxNjc3NzIxNSZ0aGlzLmZnO2RlZmF1bHQ6cmV0dXJuLTF9fSxlLnByb3RvdHlwZS5nZXRCZ0NvbG9yPWZ1bmN0aW9uKCl7c3dpdGNoKDUwMzMxNjQ4JnRoaXMuYmcpe2Nhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpyZXR1cm4gMjU1JnRoaXMuYmc7Y2FzZSA1MDMzMTY0ODpyZXR1cm4gMTY3NzcyMTUmdGhpcy5iZztkZWZhdWx0OnJldHVybi0xfX0sZS5wcm90b3R5cGUuaGFzRXh0ZW5kZWRBdHRycz1mdW5jdGlvbigpe3JldHVybiAyNjg0MzU0NTYmdGhpcy5iZ30sZS5wcm90b3R5cGUudXBkYXRlRXh0ZW5kZWQ9ZnVuY3Rpb24oKXt0aGlzLmV4dGVuZGVkLmlzRW1wdHkoKT90aGlzLmJnJj0tMjY4NDM1NDU3OnRoaXMuYmd8PTI2ODQzNTQ1Nn0sZS5wcm90b3R5cGUuZ2V0VW5kZXJsaW5lQ29sb3I9ZnVuY3Rpb24oKXtpZigyNjg0MzU0NTYmdGhpcy5iZyYmfnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3Ipc3dpdGNoKDUwMzMxNjQ4JnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3Ipe2Nhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpyZXR1cm4gMjU1JnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3I7Y2FzZSA1MDMzMTY0ODpyZXR1cm4gMTY3NzcyMTUmdGhpcy5leHRlbmRlZC51bmRlcmxpbmVDb2xvcjtkZWZhdWx0OnJldHVybiB0aGlzLmdldEZnQ29sb3IoKX1yZXR1cm4gdGhpcy5nZXRGZ0NvbG9yKCl9LGUucHJvdG90eXBlLmdldFVuZGVybGluZUNvbG9yTW9kZT1mdW5jdGlvbigpe3JldHVybiAyNjg0MzU0NTYmdGhpcy5iZyYmfnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3I/NTAzMzE2NDgmdGhpcy5leHRlbmRlZC51bmRlcmxpbmVDb2xvcjp0aGlzLmdldEZnQ29sb3JNb2RlKCl9LGUucHJvdG90eXBlLmlzVW5kZXJsaW5lQ29sb3JSR0I9ZnVuY3Rpb24oKXtyZXR1cm4gMjY4NDM1NDU2JnRoaXMuYmcmJn50aGlzLmV4dGVuZGVkLnVuZGVybGluZUNvbG9yPzUwMzMxNjQ4PT0oNTAzMzE2NDgmdGhpcy5leHRlbmRlZC51bmRlcmxpbmVDb2xvcik6dGhpcy5pc0ZnUkdCKCl9LGUucHJvdG90eXBlLmlzVW5kZXJsaW5lQ29sb3JQYWxldHRlPWZ1bmN0aW9uKCl7cmV0dXJuIDI2ODQzNTQ1NiZ0aGlzLmJnJiZ+dGhpcy5leHRlbmRlZC51bmRlcmxpbmVDb2xvcj8xNjc3NzIxNj09KDUwMzMxNjQ4JnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3IpfHwzMzU1NDQzMj09KDUwMzMxNjQ4JnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3IpOnRoaXMuaXNGZ1BhbGV0dGUoKX0sZS5wcm90b3R5cGUuaXNVbmRlcmxpbmVDb2xvckRlZmF1bHQ9ZnVuY3Rpb24oKXtyZXR1cm4gMjY4NDM1NDU2JnRoaXMuYmcmJn50aGlzLmV4dGVuZGVkLnVuZGVybGluZUNvbG9yPzA9PSg1MDMzMTY0OCZ0aGlzLmV4dGVuZGVkLnVuZGVybGluZUNvbG9yKTp0aGlzLmlzRmdEZWZhdWx0KCl9LGUucHJvdG90eXBlLmdldFVuZGVybGluZVN0eWxlPWZ1bmN0aW9uKCl7cmV0dXJuIDI2ODQzNTQ1NiZ0aGlzLmZnPzI2ODQzNTQ1NiZ0aGlzLmJnP3RoaXMuZXh0ZW5kZWQudW5kZXJsaW5lU3R5bGU6MTowfSxlfSgpO3QuQXR0cmlidXRlRGF0YT1yO3ZhciBpPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQpe3ZvaWQgMD09PWUmJihlPTApLHZvaWQgMD09PXQmJih0PS0xKSx0aGlzLnVuZGVybGluZVN0eWxlPWUsdGhpcy51bmRlcmxpbmVDb2xvcj10fXJldHVybiBlLnByb3RvdHlwZS5jbG9uZT1mdW5jdGlvbigpe3JldHVybiBuZXcgZSh0aGlzLnVuZGVybGluZVN0eWxlLHRoaXMudW5kZXJsaW5lQ29sb3IpfSxlLnByb3RvdHlwZS5pc0VtcHR5PWZ1bmN0aW9uKCl7cmV0dXJuIDA9PT10aGlzLnVuZGVybGluZVN0eWxlfSxlfSgpO3QuRXh0ZW5kZWRBdHRycz1pfSw5MDkyOihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5CdWZmZXJTdHJpbmdJdGVyYXRvcj10LkJ1ZmZlcj10Lk1BWF9CVUZGRVJfU0laRT12b2lkIDA7dmFyIGk9cig2MzQ5KSxuPXIoODQzNyksbz1yKDUxMSkscz1yKDY0MyksYT1yKDQ2MzQpLGM9cig0ODYzKSxsPXIoNzExNiksdT1yKDM3MzQpO3QuTUFYX0JVRkZFUl9TSVpFPTQyOTQ5NjcyOTU7dmFyIGg9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyKXt0aGlzLl9oYXNTY3JvbGxiYWNrPWUsdGhpcy5fb3B0aW9uc1NlcnZpY2U9dCx0aGlzLl9idWZmZXJTZXJ2aWNlPXIsdGhpcy55ZGlzcD0wLHRoaXMueWJhc2U9MCx0aGlzLnk9MCx0aGlzLng9MCx0aGlzLnNhdmVkWT0wLHRoaXMuc2F2ZWRYPTAsdGhpcy5zYXZlZEN1ckF0dHJEYXRhPW4uREVGQVVMVF9BVFRSX0RBVEEuY2xvbmUoKSx0aGlzLnNhdmVkQ2hhcnNldD1sLkRFRkFVTFRfQ0hBUlNFVCx0aGlzLm1hcmtlcnM9W10sdGhpcy5fbnVsbENlbGw9by5DZWxsRGF0YS5mcm9tQ2hhckRhdGEoWzAscy5OVUxMX0NFTExfQ0hBUixzLk5VTExfQ0VMTF9XSURUSCxzLk5VTExfQ0VMTF9DT0RFXSksdGhpcy5fd2hpdGVzcGFjZUNlbGw9by5DZWxsRGF0YS5mcm9tQ2hhckRhdGEoWzAscy5XSElURVNQQUNFX0NFTExfQ0hBUixzLldISVRFU1BBQ0VfQ0VMTF9XSURUSCxzLldISVRFU1BBQ0VfQ0VMTF9DT0RFXSksdGhpcy5fY29scz10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fcm93cz10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MsdGhpcy5saW5lcz1uZXcgaS5DaXJjdWxhckxpc3QodGhpcy5fZ2V0Q29ycmVjdEJ1ZmZlckxlbmd0aCh0aGlzLl9yb3dzKSksdGhpcy5zY3JvbGxUb3A9MCx0aGlzLnNjcm9sbEJvdHRvbT10aGlzLl9yb3dzLTEsdGhpcy5zZXR1cFRhYlN0b3BzKCl9cmV0dXJuIGUucHJvdG90eXBlLmdldE51bGxDZWxsPWZ1bmN0aW9uKGUpe3JldHVybiBlPyh0aGlzLl9udWxsQ2VsbC5mZz1lLmZnLHRoaXMuX251bGxDZWxsLmJnPWUuYmcsdGhpcy5fbnVsbENlbGwuZXh0ZW5kZWQ9ZS5leHRlbmRlZCk6KHRoaXMuX251bGxDZWxsLmZnPTAsdGhpcy5fbnVsbENlbGwuYmc9MCx0aGlzLl9udWxsQ2VsbC5leHRlbmRlZD1uZXcgdS5FeHRlbmRlZEF0dHJzKSx0aGlzLl9udWxsQ2VsbH0sZS5wcm90b3R5cGUuZ2V0V2hpdGVzcGFjZUNlbGw9ZnVuY3Rpb24oZSl7cmV0dXJuIGU/KHRoaXMuX3doaXRlc3BhY2VDZWxsLmZnPWUuZmcsdGhpcy5fd2hpdGVzcGFjZUNlbGwuYmc9ZS5iZyx0aGlzLl93aGl0ZXNwYWNlQ2VsbC5leHRlbmRlZD1lLmV4dGVuZGVkKToodGhpcy5fd2hpdGVzcGFjZUNlbGwuZmc9MCx0aGlzLl93aGl0ZXNwYWNlQ2VsbC5iZz0wLHRoaXMuX3doaXRlc3BhY2VDZWxsLmV4dGVuZGVkPW5ldyB1LkV4dGVuZGVkQXR0cnMpLHRoaXMuX3doaXRlc3BhY2VDZWxsfSxlLnByb3RvdHlwZS5nZXRCbGFua0xpbmU9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gbmV3IG4uQnVmZmVyTGluZSh0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5nZXROdWxsQ2VsbChlKSx0KX0sT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJoYXNTY3JvbGxiYWNrIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2hhc1Njcm9sbGJhY2smJnRoaXMubGluZXMubWF4TGVuZ3RoPnRoaXMuX3Jvd3N9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJpc0N1cnNvckluVmlld3BvcnQiLHtnZXQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLnliYXNlK3RoaXMueS10aGlzLnlkaXNwO3JldHVybiBlPj0wJiZlPHRoaXMuX3Jvd3N9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUuX2dldENvcnJlY3RCdWZmZXJMZW5ndGg9ZnVuY3Rpb24oZSl7aWYoIXRoaXMuX2hhc1Njcm9sbGJhY2spcmV0dXJuIGU7dmFyIHI9ZSt0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcm9sbGJhY2s7cmV0dXJuIHI+dC5NQVhfQlVGRkVSX1NJWkU/dC5NQVhfQlVGRkVSX1NJWkU6cn0sZS5wcm90b3R5cGUuZmlsbFZpZXdwb3J0Um93cz1mdW5jdGlvbihlKXtpZigwPT09dGhpcy5saW5lcy5sZW5ndGgpe3ZvaWQgMD09PWUmJihlPW4uREVGQVVMVF9BVFRSX0RBVEEpO2Zvcih2YXIgdD10aGlzLl9yb3dzO3QtLTspdGhpcy5saW5lcy5wdXNoKHRoaXMuZ2V0QmxhbmtMaW5lKGUpKX19LGUucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7dGhpcy55ZGlzcD0wLHRoaXMueWJhc2U9MCx0aGlzLnk9MCx0aGlzLng9MCx0aGlzLmxpbmVzPW5ldyBpLkNpcmN1bGFyTGlzdCh0aGlzLl9nZXRDb3JyZWN0QnVmZmVyTGVuZ3RoKHRoaXMuX3Jvd3MpKSx0aGlzLnNjcm9sbFRvcD0wLHRoaXMuc2Nyb2xsQm90dG9tPXRoaXMuX3Jvd3MtMSx0aGlzLnNldHVwVGFiU3RvcHMoKX0sZS5wcm90b3R5cGUucmVzaXplPWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5nZXROdWxsQ2VsbChuLkRFRkFVTFRfQVRUUl9EQVRBKSxpPXRoaXMuX2dldENvcnJlY3RCdWZmZXJMZW5ndGgodCk7aWYoaT50aGlzLmxpbmVzLm1heExlbmd0aCYmKHRoaXMubGluZXMubWF4TGVuZ3RoPWkpLHRoaXMubGluZXMubGVuZ3RoPjApe2lmKHRoaXMuX2NvbHM8ZSlmb3IodmFyIG89MDtvPHRoaXMubGluZXMubGVuZ3RoO28rKyl0aGlzLmxpbmVzLmdldChvKS5yZXNpemUoZSxyKTt2YXIgcz0wO2lmKHRoaXMuX3Jvd3M8dClmb3IodmFyIGE9dGhpcy5fcm93czthPHQ7YSsrKXRoaXMubGluZXMubGVuZ3RoPHQrdGhpcy55YmFzZSYmKHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMud2luZG93c01vZGU/dGhpcy5saW5lcy5wdXNoKG5ldyBuLkJ1ZmZlckxpbmUoZSxyKSk6dGhpcy55YmFzZT4wJiZ0aGlzLmxpbmVzLmxlbmd0aDw9dGhpcy55YmFzZSt0aGlzLnkrcysxPyh0aGlzLnliYXNlLS0scysrLHRoaXMueWRpc3A+MCYmdGhpcy55ZGlzcC0tKTp0aGlzLmxpbmVzLnB1c2gobmV3IG4uQnVmZmVyTGluZShlLHIpKSk7ZWxzZSBmb3IoYT10aGlzLl9yb3dzO2E+dDthLS0pdGhpcy5saW5lcy5sZW5ndGg+dCt0aGlzLnliYXNlJiYodGhpcy5saW5lcy5sZW5ndGg+dGhpcy55YmFzZSt0aGlzLnkrMT90aGlzLmxpbmVzLnBvcCgpOih0aGlzLnliYXNlKyssdGhpcy55ZGlzcCsrKSk7aWYoaTx0aGlzLmxpbmVzLm1heExlbmd0aCl7dmFyIGM9dGhpcy5saW5lcy5sZW5ndGgtaTtjPjAmJih0aGlzLmxpbmVzLnRyaW1TdGFydChjKSx0aGlzLnliYXNlPU1hdGgubWF4KHRoaXMueWJhc2UtYywwKSx0aGlzLnlkaXNwPU1hdGgubWF4KHRoaXMueWRpc3AtYywwKSx0aGlzLnNhdmVkWT1NYXRoLm1heCh0aGlzLnNhdmVkWS1jLDApKSx0aGlzLmxpbmVzLm1heExlbmd0aD1pfXRoaXMueD1NYXRoLm1pbih0aGlzLngsZS0xKSx0aGlzLnk9TWF0aC5taW4odGhpcy55LHQtMSkscyYmKHRoaXMueSs9cyksdGhpcy5zYXZlZFg9TWF0aC5taW4odGhpcy5zYXZlZFgsZS0xKSx0aGlzLnNjcm9sbFRvcD0wfWlmKHRoaXMuc2Nyb2xsQm90dG9tPXQtMSx0aGlzLl9pc1JlZmxvd0VuYWJsZWQmJih0aGlzLl9yZWZsb3coZSx0KSx0aGlzLl9jb2xzPmUpKWZvcihvPTA7bzx0aGlzLmxpbmVzLmxlbmd0aDtvKyspdGhpcy5saW5lcy5nZXQobykucmVzaXplKGUscik7dGhpcy5fY29scz1lLHRoaXMuX3Jvd3M9dH0sT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJfaXNSZWZsb3dFbmFibGVkIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2hhc1Njcm9sbGJhY2smJiF0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLndpbmRvd3NNb2RlfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLl9yZWZsb3c9ZnVuY3Rpb24oZSx0KXt0aGlzLl9jb2xzIT09ZSYmKGU+dGhpcy5fY29scz90aGlzLl9yZWZsb3dMYXJnZXIoZSx0KTp0aGlzLl9yZWZsb3dTbWFsbGVyKGUsdCkpfSxlLnByb3RvdHlwZS5fcmVmbG93TGFyZ2VyPWZ1bmN0aW9uKGUsdCl7dmFyIHI9KDAsYS5yZWZsb3dMYXJnZXJHZXRMaW5lc1RvUmVtb3ZlKSh0aGlzLmxpbmVzLHRoaXMuX2NvbHMsZSx0aGlzLnliYXNlK3RoaXMueSx0aGlzLmdldE51bGxDZWxsKG4uREVGQVVMVF9BVFRSX0RBVEEpKTtpZihyLmxlbmd0aD4wKXt2YXIgaT0oMCxhLnJlZmxvd0xhcmdlckNyZWF0ZU5ld0xheW91dCkodGhpcy5saW5lcyxyKTsoMCxhLnJlZmxvd0xhcmdlckFwcGx5TmV3TGF5b3V0KSh0aGlzLmxpbmVzLGkubGF5b3V0KSx0aGlzLl9yZWZsb3dMYXJnZXJBZGp1c3RWaWV3cG9ydChlLHQsaS5jb3VudFJlbW92ZWQpfX0sZS5wcm90b3R5cGUuX3JlZmxvd0xhcmdlckFkanVzdFZpZXdwb3J0PWZ1bmN0aW9uKGUsdCxyKXtmb3IodmFyIGk9dGhpcy5nZXROdWxsQ2VsbChuLkRFRkFVTFRfQVRUUl9EQVRBKSxvPXI7by0tID4wOykwPT09dGhpcy55YmFzZT8odGhpcy55PjAmJnRoaXMueS0tLHRoaXMubGluZXMubGVuZ3RoPHQmJnRoaXMubGluZXMucHVzaChuZXcgbi5CdWZmZXJMaW5lKGUsaSkpKToodGhpcy55ZGlzcD09PXRoaXMueWJhc2UmJnRoaXMueWRpc3AtLSx0aGlzLnliYXNlLS0pO3RoaXMuc2F2ZWRZPU1hdGgubWF4KHRoaXMuc2F2ZWRZLXIsMCl9LGUucHJvdG90eXBlLl9yZWZsb3dTbWFsbGVyPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRoaXMuZ2V0TnVsbENlbGwobi5ERUZBVUxUX0FUVFJfREFUQSksaT1bXSxvPTAscz10aGlzLmxpbmVzLmxlbmd0aC0xO3M+PTA7cy0tKXt2YXIgYz10aGlzLmxpbmVzLmdldChzKTtpZighKCFjfHwhYy5pc1dyYXBwZWQmJmMuZ2V0VHJpbW1lZExlbmd0aCgpPD1lKSl7Zm9yKHZhciBsPVtjXTtjLmlzV3JhcHBlZCYmcz4wOyljPXRoaXMubGluZXMuZ2V0KC0tcyksbC51bnNoaWZ0KGMpO3ZhciB1PXRoaXMueWJhc2UrdGhpcy55O2lmKCEodT49cyYmdTxzK2wubGVuZ3RoKSl7dmFyIGgsZj1sW2wubGVuZ3RoLTFdLmdldFRyaW1tZWRMZW5ndGgoKSxfPSgwLGEucmVmbG93U21hbGxlckdldE5ld0xpbmVMZW5ndGhzKShsLHRoaXMuX2NvbHMsZSksZD1fLmxlbmd0aC1sLmxlbmd0aDtoPTA9PT10aGlzLnliYXNlJiZ0aGlzLnkhPT10aGlzLmxpbmVzLmxlbmd0aC0xP01hdGgubWF4KDAsdGhpcy55LXRoaXMubGluZXMubWF4TGVuZ3RoK2QpOk1hdGgubWF4KDAsdGhpcy5saW5lcy5sZW5ndGgtdGhpcy5saW5lcy5tYXhMZW5ndGgrZCk7Zm9yKHZhciBwPVtdLHY9MDt2PGQ7disrKXt2YXIgZz10aGlzLmdldEJsYW5rTGluZShuLkRFRkFVTFRfQVRUUl9EQVRBLCEwKTtwLnB1c2goZyl9cC5sZW5ndGg+MCYmKGkucHVzaCh7c3RhcnQ6cytsLmxlbmd0aCtvLG5ld0xpbmVzOnB9KSxvKz1wLmxlbmd0aCksbC5wdXNoLmFwcGx5KGwscCk7dmFyIHk9Xy5sZW5ndGgtMSxtPV9beV07MD09PW0mJihtPV9bLS15XSk7Zm9yKHZhciBiPWwubGVuZ3RoLWQtMSxTPWY7Yj49MDspe3ZhciBDPU1hdGgubWluKFMsbSk7aWYobFt5XS5jb3B5Q2VsbHNGcm9tKGxbYl0sUy1DLG0tQyxDLCEwKSwwPT0obS09QykmJihtPV9bLS15XSksMD09KFMtPUMpKXtiLS07dmFyIHc9TWF0aC5tYXgoYiwwKTtTPSgwLGEuZ2V0V3JhcHBlZExpbmVUcmltbWVkTGVuZ3RoKShsLHcsdGhpcy5fY29scyl9fWZvcih2PTA7djxsLmxlbmd0aDt2KyspX1t2XTxlJiZsW3ZdLnNldENlbGwoX1t2XSxyKTtmb3IodmFyIEw9ZC1oO0wtLSA+MDspMD09PXRoaXMueWJhc2U/dGhpcy55PHQtMT8odGhpcy55KyssdGhpcy5saW5lcy5wb3AoKSk6KHRoaXMueWJhc2UrKyx0aGlzLnlkaXNwKyspOnRoaXMueWJhc2U8TWF0aC5taW4odGhpcy5saW5lcy5tYXhMZW5ndGgsdGhpcy5saW5lcy5sZW5ndGgrbyktdCYmKHRoaXMueWJhc2U9PT10aGlzLnlkaXNwJiZ0aGlzLnlkaXNwKyssdGhpcy55YmFzZSsrKTt0aGlzLnNhdmVkWT1NYXRoLm1pbih0aGlzLnNhdmVkWStkLHRoaXMueWJhc2UrdC0xKX19fWlmKGkubGVuZ3RoPjApe3ZhciBFPVtdLHg9W107Zm9yKHY9MDt2PHRoaXMubGluZXMubGVuZ3RoO3YrKyl4LnB1c2godGhpcy5saW5lcy5nZXQodikpO3ZhciBBPXRoaXMubGluZXMubGVuZ3RoLGs9QS0xLE09MCxSPWlbTV07dGhpcy5saW5lcy5sZW5ndGg9TWF0aC5taW4odGhpcy5saW5lcy5tYXhMZW5ndGgsdGhpcy5saW5lcy5sZW5ndGgrbyk7dmFyIFQ9MDtmb3Iodj1NYXRoLm1pbih0aGlzLmxpbmVzLm1heExlbmd0aC0xLEErby0xKTt2Pj0wO3YtLSlpZihSJiZSLnN0YXJ0PmsrVCl7Zm9yKHZhciBPPVIubmV3TGluZXMubGVuZ3RoLTE7Tz49MDtPLS0pdGhpcy5saW5lcy5zZXQodi0tLFIubmV3TGluZXNbT10pO3YrKyxFLnB1c2goe2luZGV4OmsrMSxhbW91bnQ6Ui5uZXdMaW5lcy5sZW5ndGh9KSxUKz1SLm5ld0xpbmVzLmxlbmd0aCxSPWlbKytNXX1lbHNlIHRoaXMubGluZXMuc2V0KHYseFtrLS1dKTt2YXIgQj0wO2Zvcih2PUUubGVuZ3RoLTE7dj49MDt2LS0pRVt2XS5pbmRleCs9Qix0aGlzLmxpbmVzLm9uSW5zZXJ0RW1pdHRlci5maXJlKEVbdl0pLEIrPUVbdl0uYW1vdW50O3ZhciBEPU1hdGgubWF4KDAsQStvLXRoaXMubGluZXMubWF4TGVuZ3RoKTtEPjAmJnRoaXMubGluZXMub25UcmltRW1pdHRlci5maXJlKEQpfX0sZS5wcm90b3R5cGUuc3RyaW5nSW5kZXhUb0J1ZmZlckluZGV4PWZ1bmN0aW9uKGUsdCxyKXtmb3Iodm9pZCAwPT09ciYmKHI9ITEpO3Q7KXt2YXIgaT10aGlzLmxpbmVzLmdldChlKTtpZighaSlyZXR1cm5bLTEsLTFdO2Zvcih2YXIgbj1yP2kuZ2V0VHJpbW1lZExlbmd0aCgpOmkubGVuZ3RoLG89MDtvPG47KytvKWlmKGkuZ2V0KG8pW3MuQ0hBUl9EQVRBX1dJRFRIX0lOREVYXSYmKHQtPWkuZ2V0KG8pW3MuQ0hBUl9EQVRBX0NIQVJfSU5ERVhdLmxlbmd0aHx8MSksdDwwKXJldHVybltlLG9dO2UrK31yZXR1cm5bZSwwXX0sZS5wcm90b3R5cGUudHJhbnNsYXRlQnVmZmVyTGluZVRvU3RyaW5nPWZ1bmN0aW9uKGUsdCxyLGkpe3ZvaWQgMD09PXImJihyPTApO3ZhciBuPXRoaXMubGluZXMuZ2V0KGUpO3JldHVybiBuP24udHJhbnNsYXRlVG9TdHJpbmcodCxyLGkpOiIifSxlLnByb3RvdHlwZS5nZXRXcmFwcGVkUmFuZ2VGb3JMaW5lPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1lLHI9ZTt0PjAmJnRoaXMubGluZXMuZ2V0KHQpLmlzV3JhcHBlZDspdC0tO2Zvcig7cisxPHRoaXMubGluZXMubGVuZ3RoJiZ0aGlzLmxpbmVzLmdldChyKzEpLmlzV3JhcHBlZDspcisrO3JldHVybntmaXJzdDp0LGxhc3Q6cn19LGUucHJvdG90eXBlLnNldHVwVGFiU3RvcHM9ZnVuY3Rpb24oZSl7Zm9yKG51bGwhPWU/dGhpcy50YWJzW2VdfHwoZT10aGlzLnByZXZTdG9wKGUpKToodGhpcy50YWJzPXt9LGU9MCk7ZTx0aGlzLl9jb2xzO2UrPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMudGFiU3RvcFdpZHRoKXRoaXMudGFic1tlXT0hMH0sZS5wcm90b3R5cGUucHJldlN0b3A9ZnVuY3Rpb24oZSl7Zm9yKG51bGw9PWUmJihlPXRoaXMueCk7IXRoaXMudGFic1stLWVdJiZlPjA7KTtyZXR1cm4gZT49dGhpcy5fY29scz90aGlzLl9jb2xzLTE6ZTwwPzA6ZX0sZS5wcm90b3R5cGUubmV4dFN0b3A9ZnVuY3Rpb24oZSl7Zm9yKG51bGw9PWUmJihlPXRoaXMueCk7IXRoaXMudGFic1srK2VdJiZlPHRoaXMuX2NvbHM7KTtyZXR1cm4gZT49dGhpcy5fY29scz90aGlzLl9jb2xzLTE6ZTwwPzA6ZX0sZS5wcm90b3R5cGUuYWRkTWFya2VyPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMscj1uZXcgYy5NYXJrZXIoZSk7cmV0dXJuIHRoaXMubWFya2Vycy5wdXNoKHIpLHIucmVnaXN0ZXIodGhpcy5saW5lcy5vblRyaW0oKGZ1bmN0aW9uKGUpe3IubGluZS09ZSxyLmxpbmU8MCYmci5kaXNwb3NlKCl9KSkpLHIucmVnaXN0ZXIodGhpcy5saW5lcy5vbkluc2VydCgoZnVuY3Rpb24oZSl7ci5saW5lPj1lLmluZGV4JiYoci5saW5lKz1lLmFtb3VudCl9KSkpLHIucmVnaXN0ZXIodGhpcy5saW5lcy5vbkRlbGV0ZSgoZnVuY3Rpb24oZSl7ci5saW5lPj1lLmluZGV4JiZyLmxpbmU8ZS5pbmRleCtlLmFtb3VudCYmci5kaXNwb3NlKCksci5saW5lPmUuaW5kZXgmJihyLmxpbmUtPWUuYW1vdW50KX0pKSksci5yZWdpc3RlcihyLm9uRGlzcG9zZSgoZnVuY3Rpb24oKXtyZXR1cm4gdC5fcmVtb3ZlTWFya2VyKHIpfSkpKSxyfSxlLnByb3RvdHlwZS5fcmVtb3ZlTWFya2VyPWZ1bmN0aW9uKGUpe3RoaXMubWFya2Vycy5zcGxpY2UodGhpcy5tYXJrZXJzLmluZGV4T2YoZSksMSl9LGUucHJvdG90eXBlLml0ZXJhdG9yPWZ1bmN0aW9uKGUsdCxyLGksbil7cmV0dXJuIG5ldyBmKHRoaXMsZSx0LHIsaSxuKX0sZX0oKTt0LkJ1ZmZlcj1oO3ZhciBmPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQscixpLG4sbyl7dm9pZCAwPT09ciYmKHI9MCksdm9pZCAwPT09aSYmKGk9ZS5saW5lcy5sZW5ndGgpLHZvaWQgMD09PW4mJihuPTApLHZvaWQgMD09PW8mJihvPTApLHRoaXMuX2J1ZmZlcj1lLHRoaXMuX3RyaW1SaWdodD10LHRoaXMuX3N0YXJ0SW5kZXg9cix0aGlzLl9lbmRJbmRleD1pLHRoaXMuX3N0YXJ0T3ZlcnNjYW49bix0aGlzLl9lbmRPdmVyc2Nhbj1vLHRoaXMuX3N0YXJ0SW5kZXg8MCYmKHRoaXMuX3N0YXJ0SW5kZXg9MCksdGhpcy5fZW5kSW5kZXg+dGhpcy5fYnVmZmVyLmxpbmVzLmxlbmd0aCYmKHRoaXMuX2VuZEluZGV4PXRoaXMuX2J1ZmZlci5saW5lcy5sZW5ndGgpLHRoaXMuX2N1cnJlbnQ9dGhpcy5fc3RhcnRJbmRleH1yZXR1cm4gZS5wcm90b3R5cGUuaGFzTmV4dD1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jdXJyZW50PHRoaXMuX2VuZEluZGV4fSxlLnByb3RvdHlwZS5uZXh0PWZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5fYnVmZmVyLmdldFdyYXBwZWRSYW5nZUZvckxpbmUodGhpcy5fY3VycmVudCk7ZS5maXJzdDx0aGlzLl9zdGFydEluZGV4LXRoaXMuX3N0YXJ0T3ZlcnNjYW4mJihlLmZpcnN0PXRoaXMuX3N0YXJ0SW5kZXgtdGhpcy5fc3RhcnRPdmVyc2NhbiksZS5sYXN0PnRoaXMuX2VuZEluZGV4K3RoaXMuX2VuZE92ZXJzY2FuJiYoZS5sYXN0PXRoaXMuX2VuZEluZGV4K3RoaXMuX2VuZE92ZXJzY2FuKSxlLmZpcnN0PU1hdGgubWF4KGUuZmlyc3QsMCksZS5sYXN0PU1hdGgubWluKGUubGFzdCx0aGlzLl9idWZmZXIubGluZXMubGVuZ3RoKTtmb3IodmFyIHQ9IiIscj1lLmZpcnN0O3I8PWUubGFzdDsrK3IpdCs9dGhpcy5fYnVmZmVyLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhyLHRoaXMuX3RyaW1SaWdodCk7cmV0dXJuIHRoaXMuX2N1cnJlbnQ9ZS5sYXN0KzEse3JhbmdlOmUsY29udGVudDp0fX0sZX0oKTt0LkJ1ZmZlclN0cmluZ0l0ZXJhdG9yPWZ9LDg0Mzc6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJ1ZmZlckxpbmU9dC5ERUZBVUxUX0FUVFJfREFUQT12b2lkIDA7dmFyIGk9cig0ODIpLG49cig2NDMpLG89cig1MTEpLHM9cigzNzM0KTt0LkRFRkFVTFRfQVRUUl9EQVRBPU9iamVjdC5mcmVlemUobmV3IHMuQXR0cmlidXRlRGF0YSk7dmFyIGE9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyKXt2b2lkIDA9PT1yJiYocj0hMSksdGhpcy5pc1dyYXBwZWQ9cix0aGlzLl9jb21iaW5lZD17fSx0aGlzLl9leHRlbmRlZEF0dHJzPXt9LHRoaXMuX2RhdGE9bmV3IFVpbnQzMkFycmF5KDMqZSk7Zm9yKHZhciBpPXR8fG8uQ2VsbERhdGEuZnJvbUNoYXJEYXRhKFswLG4uTlVMTF9DRUxMX0NIQVIsbi5OVUxMX0NFTExfV0lEVEgsbi5OVUxMX0NFTExfQ09ERV0pLHM9MDtzPGU7KytzKXRoaXMuc2V0Q2VsbChzLGkpO3RoaXMubGVuZ3RoPWV9cmV0dXJuIGUucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9kYXRhWzMqZSswXSxyPTIwOTcxNTEmdDtyZXR1cm5bdGhpcy5fZGF0YVszKmUrMV0sMjA5NzE1MiZ0P3RoaXMuX2NvbWJpbmVkW2VdOnI/KDAsaS5zdHJpbmdGcm9tQ29kZVBvaW50KShyKToiIix0Pj4yMiwyMDk3MTUyJnQ/dGhpcy5fY29tYmluZWRbZV0uY2hhckNvZGVBdCh0aGlzLl9jb21iaW5lZFtlXS5sZW5ndGgtMSk6cl19LGUucHJvdG90eXBlLnNldD1mdW5jdGlvbihlLHQpe3RoaXMuX2RhdGFbMyplKzFdPXRbbi5DSEFSX0RBVEFfQVRUUl9JTkRFWF0sdFtuLkNIQVJfREFUQV9DSEFSX0lOREVYXS5sZW5ndGg+MT8odGhpcy5fY29tYmluZWRbZV09dFsxXSx0aGlzLl9kYXRhWzMqZSswXT0yMDk3MTUyfGV8dFtuLkNIQVJfREFUQV9XSURUSF9JTkRFWF08PDIyKTp0aGlzLl9kYXRhWzMqZSswXT10W24uQ0hBUl9EQVRBX0NIQVJfSU5ERVhdLmNoYXJDb2RlQXQoMCl8dFtuLkNIQVJfREFUQV9XSURUSF9JTkRFWF08PDIyfSxlLnByb3RvdHlwZS5nZXRXaWR0aD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fZGF0YVszKmUrMF0+PjIyfSxlLnByb3RvdHlwZS5oYXNXaWR0aD1mdW5jdGlvbihlKXtyZXR1cm4gMTI1ODI5MTImdGhpcy5fZGF0YVszKmUrMF19LGUucHJvdG90eXBlLmdldEZnPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9kYXRhWzMqZSsxXX0sZS5wcm90b3R5cGUuZ2V0Qmc9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2RhdGFbMyplKzJdfSxlLnByb3RvdHlwZS5oYXNDb250ZW50PWZ1bmN0aW9uKGUpe3JldHVybiA0MTk0MzAzJnRoaXMuX2RhdGFbMyplKzBdfSxlLnByb3RvdHlwZS5nZXRDb2RlUG9pbnQ9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fZGF0YVszKmUrMF07cmV0dXJuIDIwOTcxNTImdD90aGlzLl9jb21iaW5lZFtlXS5jaGFyQ29kZUF0KHRoaXMuX2NvbWJpbmVkW2VdLmxlbmd0aC0xKToyMDk3MTUxJnR9LGUucHJvdG90eXBlLmlzQ29tYmluZWQ9ZnVuY3Rpb24oZSl7cmV0dXJuIDIwOTcxNTImdGhpcy5fZGF0YVszKmUrMF19LGUucHJvdG90eXBlLmdldFN0cmluZz1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9kYXRhWzMqZSswXTtyZXR1cm4gMjA5NzE1MiZ0P3RoaXMuX2NvbWJpbmVkW2VdOjIwOTcxNTEmdD8oMCxpLnN0cmluZ0Zyb21Db2RlUG9pbnQpKDIwOTcxNTEmdCk6IiJ9LGUucHJvdG90eXBlLmxvYWRDZWxsPWZ1bmN0aW9uKGUsdCl7dmFyIHI9MyplO3JldHVybiB0LmNvbnRlbnQ9dGhpcy5fZGF0YVtyKzBdLHQuZmc9dGhpcy5fZGF0YVtyKzFdLHQuYmc9dGhpcy5fZGF0YVtyKzJdLDIwOTcxNTImdC5jb250ZW50JiYodC5jb21iaW5lZERhdGE9dGhpcy5fY29tYmluZWRbZV0pLDI2ODQzNTQ1NiZ0LmJnJiYodC5leHRlbmRlZD10aGlzLl9leHRlbmRlZEF0dHJzW2VdKSx0fSxlLnByb3RvdHlwZS5zZXRDZWxsPWZ1bmN0aW9uKGUsdCl7MjA5NzE1MiZ0LmNvbnRlbnQmJih0aGlzLl9jb21iaW5lZFtlXT10LmNvbWJpbmVkRGF0YSksMjY4NDM1NDU2JnQuYmcmJih0aGlzLl9leHRlbmRlZEF0dHJzW2VdPXQuZXh0ZW5kZWQpLHRoaXMuX2RhdGFbMyplKzBdPXQuY29udGVudCx0aGlzLl9kYXRhWzMqZSsxXT10LmZnLHRoaXMuX2RhdGFbMyplKzJdPXQuYmd9LGUucHJvdG90eXBlLnNldENlbGxGcm9tQ29kZVBvaW50PWZ1bmN0aW9uKGUsdCxyLGksbixvKXsyNjg0MzU0NTYmbiYmKHRoaXMuX2V4dGVuZGVkQXR0cnNbZV09byksdGhpcy5fZGF0YVszKmUrMF09dHxyPDwyMix0aGlzLl9kYXRhWzMqZSsxXT1pLHRoaXMuX2RhdGFbMyplKzJdPW59LGUucHJvdG90eXBlLmFkZENvZGVwb2ludFRvQ2VsbD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX2RhdGFbMyplKzBdOzIwOTcxNTImcj90aGlzLl9jb21iaW5lZFtlXSs9KDAsaS5zdHJpbmdGcm9tQ29kZVBvaW50KSh0KTooMjA5NzE1MSZyPyh0aGlzLl9jb21iaW5lZFtlXT0oMCxpLnN0cmluZ0Zyb21Db2RlUG9pbnQpKDIwOTcxNTEmcikrKDAsaS5zdHJpbmdGcm9tQ29kZVBvaW50KSh0KSxyJj0tMjA5NzE1MixyfD0yMDk3MTUyKTpyPXR8MTw8MjIsdGhpcy5fZGF0YVszKmUrMF09cil9LGUucHJvdG90eXBlLmluc2VydENlbGxzPWZ1bmN0aW9uKGUsdCxyLGkpe2lmKChlJT10aGlzLmxlbmd0aCkmJjI9PT10aGlzLmdldFdpZHRoKGUtMSkmJnRoaXMuc2V0Q2VsbEZyb21Db2RlUG9pbnQoZS0xLDAsMSwobnVsbD09aT92b2lkIDA6aS5mZyl8fDAsKG51bGw9PWk/dm9pZCAwOmkuYmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmV4dGVuZGVkKXx8bmV3IHMuRXh0ZW5kZWRBdHRycyksdDx0aGlzLmxlbmd0aC1lKXtmb3IodmFyIG49bmV3IG8uQ2VsbERhdGEsYT10aGlzLmxlbmd0aC1lLXQtMTthPj0wOy0tYSl0aGlzLnNldENlbGwoZSt0K2EsdGhpcy5sb2FkQ2VsbChlK2EsbikpO2ZvcihhPTA7YTx0OysrYSl0aGlzLnNldENlbGwoZSthLHIpfWVsc2UgZm9yKGE9ZTthPHRoaXMubGVuZ3RoOysrYSl0aGlzLnNldENlbGwoYSxyKTsyPT09dGhpcy5nZXRXaWR0aCh0aGlzLmxlbmd0aC0xKSYmdGhpcy5zZXRDZWxsRnJvbUNvZGVQb2ludCh0aGlzLmxlbmd0aC0xLDAsMSwobnVsbD09aT92b2lkIDA6aS5mZyl8fDAsKG51bGw9PWk/dm9pZCAwOmkuYmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmV4dGVuZGVkKXx8bmV3IHMuRXh0ZW5kZWRBdHRycyl9LGUucHJvdG90eXBlLmRlbGV0ZUNlbGxzPWZ1bmN0aW9uKGUsdCxyLGkpe2lmKGUlPXRoaXMubGVuZ3RoLHQ8dGhpcy5sZW5ndGgtZSl7Zm9yKHZhciBuPW5ldyBvLkNlbGxEYXRhLGE9MDthPHRoaXMubGVuZ3RoLWUtdDsrK2EpdGhpcy5zZXRDZWxsKGUrYSx0aGlzLmxvYWRDZWxsKGUrdCthLG4pKTtmb3IoYT10aGlzLmxlbmd0aC10O2E8dGhpcy5sZW5ndGg7KythKXRoaXMuc2V0Q2VsbChhLHIpfWVsc2UgZm9yKGE9ZTthPHRoaXMubGVuZ3RoOysrYSl0aGlzLnNldENlbGwoYSxyKTtlJiYyPT09dGhpcy5nZXRXaWR0aChlLTEpJiZ0aGlzLnNldENlbGxGcm9tQ29kZVBvaW50KGUtMSwwLDEsKG51bGw9PWk/dm9pZCAwOmkuZmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmJnKXx8MCwobnVsbD09aT92b2lkIDA6aS5leHRlbmRlZCl8fG5ldyBzLkV4dGVuZGVkQXR0cnMpLDAhPT10aGlzLmdldFdpZHRoKGUpfHx0aGlzLmhhc0NvbnRlbnQoZSl8fHRoaXMuc2V0Q2VsbEZyb21Db2RlUG9pbnQoZSwwLDEsKG51bGw9PWk/dm9pZCAwOmkuZmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmJnKXx8MCwobnVsbD09aT92b2lkIDA6aS5leHRlbmRlZCl8fG5ldyBzLkV4dGVuZGVkQXR0cnMpfSxlLnByb3RvdHlwZS5yZXBsYWNlQ2VsbHM9ZnVuY3Rpb24oZSx0LHIsaSl7Zm9yKGUmJjI9PT10aGlzLmdldFdpZHRoKGUtMSkmJnRoaXMuc2V0Q2VsbEZyb21Db2RlUG9pbnQoZS0xLDAsMSwobnVsbD09aT92b2lkIDA6aS5mZyl8fDAsKG51bGw9PWk/dm9pZCAwOmkuYmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmV4dGVuZGVkKXx8bmV3IHMuRXh0ZW5kZWRBdHRycyksdDx0aGlzLmxlbmd0aCYmMj09PXRoaXMuZ2V0V2lkdGgodC0xKSYmdGhpcy5zZXRDZWxsRnJvbUNvZGVQb2ludCh0LDAsMSwobnVsbD09aT92b2lkIDA6aS5mZyl8fDAsKG51bGw9PWk/dm9pZCAwOmkuYmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmV4dGVuZGVkKXx8bmV3IHMuRXh0ZW5kZWRBdHRycyk7ZTx0JiZlPHRoaXMubGVuZ3RoOyl0aGlzLnNldENlbGwoZSsrLHIpfSxlLnByb3RvdHlwZS5yZXNpemU9ZnVuY3Rpb24oZSx0KXtpZihlIT09dGhpcy5sZW5ndGgpe2lmKGU+dGhpcy5sZW5ndGgpe3ZhciByPW5ldyBVaW50MzJBcnJheSgzKmUpO3RoaXMubGVuZ3RoJiYoMyplPHRoaXMuX2RhdGEubGVuZ3RoP3Iuc2V0KHRoaXMuX2RhdGEuc3ViYXJyYXkoMCwzKmUpKTpyLnNldCh0aGlzLl9kYXRhKSksdGhpcy5fZGF0YT1yO2Zvcih2YXIgaT10aGlzLmxlbmd0aDtpPGU7KytpKXRoaXMuc2V0Q2VsbChpLHQpfWVsc2UgaWYoZSl7KHI9bmV3IFVpbnQzMkFycmF5KDMqZSkpLnNldCh0aGlzLl9kYXRhLnN1YmFycmF5KDAsMyplKSksdGhpcy5fZGF0YT1yO3ZhciBuPU9iamVjdC5rZXlzKHRoaXMuX2NvbWJpbmVkKTtmb3IoaT0wO2k8bi5sZW5ndGg7aSsrKXt2YXIgbz1wYXJzZUludChuW2ldLDEwKTtvPj1lJiZkZWxldGUgdGhpcy5fY29tYmluZWRbb119fWVsc2UgdGhpcy5fZGF0YT1uZXcgVWludDMyQXJyYXkoMCksdGhpcy5fY29tYmluZWQ9e307dGhpcy5sZW5ndGg9ZX19LGUucHJvdG90eXBlLmZpbGw9ZnVuY3Rpb24oZSl7dGhpcy5fY29tYmluZWQ9e30sdGhpcy5fZXh0ZW5kZWRBdHRycz17fTtmb3IodmFyIHQ9MDt0PHRoaXMubGVuZ3RoOysrdCl0aGlzLnNldENlbGwodCxlKX0sZS5wcm90b3R5cGUuY29weUZyb209ZnVuY3Rpb24oZSl7Zm9yKHZhciB0IGluIHRoaXMubGVuZ3RoIT09ZS5sZW5ndGg/dGhpcy5fZGF0YT1uZXcgVWludDMyQXJyYXkoZS5fZGF0YSk6dGhpcy5fZGF0YS5zZXQoZS5fZGF0YSksdGhpcy5sZW5ndGg9ZS5sZW5ndGgsdGhpcy5fY29tYmluZWQ9e30sZS5fY29tYmluZWQpdGhpcy5fY29tYmluZWRbdF09ZS5fY29tYmluZWRbdF07Zm9yKHZhciB0IGluIHRoaXMuX2V4dGVuZGVkQXR0cnM9e30sZS5fZXh0ZW5kZWRBdHRycyl0aGlzLl9leHRlbmRlZEF0dHJzW3RdPWUuX2V4dGVuZGVkQXR0cnNbdF07dGhpcy5pc1dyYXBwZWQ9ZS5pc1dyYXBwZWR9LGUucHJvdG90eXBlLmNsb25lPWZ1bmN0aW9uKCl7dmFyIHQ9bmV3IGUoMCk7Zm9yKHZhciByIGluIHQuX2RhdGE9bmV3IFVpbnQzMkFycmF5KHRoaXMuX2RhdGEpLHQubGVuZ3RoPXRoaXMubGVuZ3RoLHRoaXMuX2NvbWJpbmVkKXQuX2NvbWJpbmVkW3JdPXRoaXMuX2NvbWJpbmVkW3JdO2Zvcih2YXIgciBpbiB0aGlzLl9leHRlbmRlZEF0dHJzKXQuX2V4dGVuZGVkQXR0cnNbcl09dGhpcy5fZXh0ZW5kZWRBdHRyc1tyXTtyZXR1cm4gdC5pc1dyYXBwZWQ9dGhpcy5pc1dyYXBwZWQsdH0sZS5wcm90b3R5cGUuZ2V0VHJpbW1lZExlbmd0aD1mdW5jdGlvbigpe2Zvcih2YXIgZT10aGlzLmxlbmd0aC0xO2U+PTA7LS1lKWlmKDQxOTQzMDMmdGhpcy5fZGF0YVszKmUrMF0pcmV0dXJuIGUrKHRoaXMuX2RhdGFbMyplKzBdPj4yMik7cmV0dXJuIDB9LGUucHJvdG90eXBlLmNvcHlDZWxsc0Zyb209ZnVuY3Rpb24oZSx0LHIsaSxuKXt2YXIgbz1lLl9kYXRhO2lmKG4pZm9yKHZhciBzPWktMTtzPj0wO3MtLSlmb3IodmFyIGE9MDthPDM7YSsrKXRoaXMuX2RhdGFbMyoocitzKSthXT1vWzMqKHQrcykrYV07ZWxzZSBmb3Iocz0wO3M8aTtzKyspZm9yKGE9MDthPDM7YSsrKXRoaXMuX2RhdGFbMyoocitzKSthXT1vWzMqKHQrcykrYV07dmFyIGM9T2JqZWN0LmtleXMoZS5fY29tYmluZWQpO2ZvcihhPTA7YTxjLmxlbmd0aDthKyspe3ZhciBsPXBhcnNlSW50KGNbYV0sMTApO2w+PXQmJih0aGlzLl9jb21iaW5lZFtsLXQrcl09ZS5fY29tYmluZWRbbF0pfX0sZS5wcm90b3R5cGUudHJhbnNsYXRlVG9TdHJpbmc9ZnVuY3Rpb24oZSx0LHIpe3ZvaWQgMD09PWUmJihlPSExKSx2b2lkIDA9PT10JiYodD0wKSx2b2lkIDA9PT1yJiYocj10aGlzLmxlbmd0aCksZSYmKHI9TWF0aC5taW4ocix0aGlzLmdldFRyaW1tZWRMZW5ndGgoKSkpO2Zvcih2YXIgbz0iIjt0PHI7KXt2YXIgcz10aGlzLl9kYXRhWzMqdCswXSxhPTIwOTcxNTEmcztvKz0yMDk3MTUyJnM/dGhpcy5fY29tYmluZWRbdF06YT8oMCxpLnN0cmluZ0Zyb21Db2RlUG9pbnQpKGEpOm4uV0hJVEVTUEFDRV9DRUxMX0NIQVIsdCs9cz4+MjJ8fDF9cmV0dXJuIG99LGV9KCk7dC5CdWZmZXJMaW5lPWF9LDQ4NDE6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5nZXRSYW5nZUxlbmd0aD12b2lkIDAsdC5nZXRSYW5nZUxlbmd0aD1mdW5jdGlvbihlLHQpe2lmKGUuc3RhcnQueT5lLmVuZC55KXRocm93IG5ldyBFcnJvcigiQnVmZmVyIHJhbmdlIGVuZCAoIitlLmVuZC54KyIsICIrZS5lbmQueSsiKSBjYW5ub3QgYmUgYmVmb3JlIHN0YXJ0ICgiK2Uuc3RhcnQueCsiLCAiK2Uuc3RhcnQueSsiKSIpO3JldHVybiB0KihlLmVuZC55LWUuc3RhcnQueSkrKGUuZW5kLngtZS5zdGFydC54KzEpfX0sNDYzNDooZSx0KT0+e2Z1bmN0aW9uIHIoZSx0LHIpe2lmKHQ9PT1lLmxlbmd0aC0xKXJldHVybiBlW3RdLmdldFRyaW1tZWRMZW5ndGgoKTt2YXIgaT0hZVt0XS5oYXNDb250ZW50KHItMSkmJjE9PT1lW3RdLmdldFdpZHRoKHItMSksbj0yPT09ZVt0KzFdLmdldFdpZHRoKDApO3JldHVybiBpJiZuP3ItMTpyfU9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LmdldFdyYXBwZWRMaW5lVHJpbW1lZExlbmd0aD10LnJlZmxvd1NtYWxsZXJHZXROZXdMaW5lTGVuZ3Rocz10LnJlZmxvd0xhcmdlckFwcGx5TmV3TGF5b3V0PXQucmVmbG93TGFyZ2VyQ3JlYXRlTmV3TGF5b3V0PXQucmVmbG93TGFyZ2VyR2V0TGluZXNUb1JlbW92ZT12b2lkIDAsdC5yZWZsb3dMYXJnZXJHZXRMaW5lc1RvUmVtb3ZlPWZ1bmN0aW9uKGUsdCxpLG4sbyl7Zm9yKHZhciBzPVtdLGE9MDthPGUubGVuZ3RoLTE7YSsrKXt2YXIgYz1hLGw9ZS5nZXQoKytjKTtpZihsLmlzV3JhcHBlZCl7Zm9yKHZhciB1PVtlLmdldChhKV07YzxlLmxlbmd0aCYmbC5pc1dyYXBwZWQ7KXUucHVzaChsKSxsPWUuZ2V0KCsrYyk7aWYobj49YSYmbjxjKWErPXUubGVuZ3RoLTE7ZWxzZXtmb3IodmFyIGg9MCxmPXIodSxoLHQpLF89MSxkPTA7Xzx1Lmxlbmd0aDspe3ZhciBwPXIodSxfLHQpLHY9cC1kLGc9aS1mLHk9TWF0aC5taW4odixnKTt1W2hdLmNvcHlDZWxsc0Zyb20odVtfXSxkLGYseSwhMSksKGYrPXkpPT09aSYmKGgrKyxmPTApLChkKz15KT09PXAmJihfKyssZD0wKSwwPT09ZiYmMCE9PWgmJjI9PT11W2gtMV0uZ2V0V2lkdGgoaS0xKSYmKHVbaF0uY29weUNlbGxzRnJvbSh1W2gtMV0saS0xLGYrKywxLCExKSx1W2gtMV0uc2V0Q2VsbChpLTEsbykpfXVbaF0ucmVwbGFjZUNlbGxzKGYsaSxvKTtmb3IodmFyIG09MCxiPXUubGVuZ3RoLTE7Yj4wJiYoYj5ofHwwPT09dVtiXS5nZXRUcmltbWVkTGVuZ3RoKCkpO2ItLSltKys7bT4wJiYocy5wdXNoKGErdS5sZW5ndGgtbSkscy5wdXNoKG0pKSxhKz11Lmxlbmd0aC0xfX19cmV0dXJuIHN9LHQucmVmbG93TGFyZ2VyQ3JlYXRlTmV3TGF5b3V0PWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPVtdLGk9MCxuPXRbaV0sbz0wLHM9MDtzPGUubGVuZ3RoO3MrKylpZihuPT09cyl7dmFyIGE9dFsrK2ldO2Uub25EZWxldGVFbWl0dGVyLmZpcmUoe2luZGV4OnMtbyxhbW91bnQ6YX0pLHMrPWEtMSxvKz1hLG49dFsrK2ldfWVsc2Ugci5wdXNoKHMpO3JldHVybntsYXlvdXQ6cixjb3VudFJlbW92ZWQ6b319LHQucmVmbG93TGFyZ2VyQXBwbHlOZXdMYXlvdXQ9ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHI9W10saT0wO2k8dC5sZW5ndGg7aSsrKXIucHVzaChlLmdldCh0W2ldKSk7Zm9yKGk9MDtpPHIubGVuZ3RoO2krKyllLnNldChpLHJbaV0pO2UubGVuZ3RoPXQubGVuZ3RofSx0LnJlZmxvd1NtYWxsZXJHZXROZXdMaW5lTGVuZ3Rocz1mdW5jdGlvbihlLHQsaSl7Zm9yKHZhciBuPVtdLG89ZS5tYXAoKGZ1bmN0aW9uKGksbil7cmV0dXJuIHIoZSxuLHQpfSkpLnJlZHVjZSgoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSt0fSkpLHM9MCxhPTAsYz0wO2M8bzspe2lmKG8tYzxpKXtuLnB1c2goby1jKTticmVha31zKz1pO3ZhciBsPXIoZSxhLHQpO3M+bCYmKHMtPWwsYSsrKTt2YXIgdT0yPT09ZVthXS5nZXRXaWR0aChzLTEpO3UmJnMtLTt2YXIgaD11P2ktMTppO24ucHVzaChoKSxjKz1ofXJldHVybiBufSx0LmdldFdyYXBwZWRMaW5lVHJpbW1lZExlbmd0aD1yfSw1Mjk1OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJ1ZmZlclNldD12b2lkIDA7dmFyIG89cig5MDkyKSxzPXIoODQ2MCksYT1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscil7dmFyIGk9ZS5jYWxsKHRoaXMpfHx0aGlzO3JldHVybiBpLl9vcHRpb25zU2VydmljZT10LGkuX2J1ZmZlclNlcnZpY2U9cixpLl9vbkJ1ZmZlckFjdGl2YXRlPWkucmVnaXN0ZXIobmV3IHMuRXZlbnRFbWl0dGVyKSxpLnJlc2V0KCksaX1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25CdWZmZXJBY3RpdmF0ZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkJ1ZmZlckFjdGl2YXRlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLHQucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5fbm9ybWFsPW5ldyBvLkJ1ZmZlcighMCx0aGlzLl9vcHRpb25zU2VydmljZSx0aGlzLl9idWZmZXJTZXJ2aWNlKSx0aGlzLl9ub3JtYWwuZmlsbFZpZXdwb3J0Um93cygpLHRoaXMuX2FsdD1uZXcgby5CdWZmZXIoITEsdGhpcy5fb3B0aW9uc1NlcnZpY2UsdGhpcy5fYnVmZmVyU2VydmljZSksdGhpcy5fYWN0aXZlQnVmZmVyPXRoaXMuX25vcm1hbCx0aGlzLl9vbkJ1ZmZlckFjdGl2YXRlLmZpcmUoe2FjdGl2ZUJ1ZmZlcjp0aGlzLl9ub3JtYWwsaW5hY3RpdmVCdWZmZXI6dGhpcy5fYWx0fSksdGhpcy5zZXR1cFRhYlN0b3BzKCl9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwiYWx0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2FsdH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsImFjdGl2ZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9hY3RpdmVCdWZmZXJ9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJub3JtYWwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fbm9ybWFsfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLHQucHJvdG90eXBlLmFjdGl2YXRlTm9ybWFsQnVmZmVyPWZ1bmN0aW9uKCl7dGhpcy5fYWN0aXZlQnVmZmVyIT09dGhpcy5fbm9ybWFsJiYodGhpcy5fbm9ybWFsLng9dGhpcy5fYWx0LngsdGhpcy5fbm9ybWFsLnk9dGhpcy5fYWx0LnksdGhpcy5fYWx0LmNsZWFyKCksdGhpcy5fYWN0aXZlQnVmZmVyPXRoaXMuX25vcm1hbCx0aGlzLl9vbkJ1ZmZlckFjdGl2YXRlLmZpcmUoe2FjdGl2ZUJ1ZmZlcjp0aGlzLl9ub3JtYWwsaW5hY3RpdmVCdWZmZXI6dGhpcy5fYWx0fSkpfSx0LnByb3RvdHlwZS5hY3RpdmF0ZUFsdEJ1ZmZlcj1mdW5jdGlvbihlKXt0aGlzLl9hY3RpdmVCdWZmZXIhPT10aGlzLl9hbHQmJih0aGlzLl9hbHQuZmlsbFZpZXdwb3J0Um93cyhlKSx0aGlzLl9hbHQueD10aGlzLl9ub3JtYWwueCx0aGlzLl9hbHQueT10aGlzLl9ub3JtYWwueSx0aGlzLl9hY3RpdmVCdWZmZXI9dGhpcy5fYWx0LHRoaXMuX29uQnVmZmVyQWN0aXZhdGUuZmlyZSh7YWN0aXZlQnVmZmVyOnRoaXMuX2FsdCxpbmFjdGl2ZUJ1ZmZlcjp0aGlzLl9ub3JtYWx9KSl9LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuX25vcm1hbC5yZXNpemUoZSx0KSx0aGlzLl9hbHQucmVzaXplKGUsdCl9LHQucHJvdG90eXBlLnNldHVwVGFiU3RvcHM9ZnVuY3Rpb24oZSl7dGhpcy5fbm9ybWFsLnNldHVwVGFiU3RvcHMoZSksdGhpcy5fYWx0LnNldHVwVGFiU3RvcHMoZSl9LHR9KHIoODQ0KS5EaXNwb3NhYmxlKTt0LkJ1ZmZlclNldD1hfSw1MTE6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQ2VsbERhdGE9dm9pZCAwO3ZhciBvPXIoNDgyKSxzPXIoNjQzKSxhPXIoMzczNCksYz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KCl7dmFyIHQ9bnVsbCE9PWUmJmUuYXBwbHkodGhpcyxhcmd1bWVudHMpfHx0aGlzO3JldHVybiB0LmNvbnRlbnQ9MCx0LmZnPTAsdC5iZz0wLHQuZXh0ZW5kZWQ9bmV3IGEuRXh0ZW5kZWRBdHRycyx0LmNvbWJpbmVkRGF0YT0iIix0fXJldHVybiBuKHQsZSksdC5mcm9tQ2hhckRhdGE9ZnVuY3Rpb24oZSl7dmFyIHI9bmV3IHQ7cmV0dXJuIHIuc2V0RnJvbUNoYXJEYXRhKGUpLHJ9LHQucHJvdG90eXBlLmlzQ29tYmluZWQ9ZnVuY3Rpb24oKXtyZXR1cm4gMjA5NzE1MiZ0aGlzLmNvbnRlbnR9LHQucHJvdG90eXBlLmdldFdpZHRoPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuY29udGVudD4+MjJ9LHQucHJvdG90eXBlLmdldENoYXJzPWZ1bmN0aW9uKCl7cmV0dXJuIDIwOTcxNTImdGhpcy5jb250ZW50P3RoaXMuY29tYmluZWREYXRhOjIwOTcxNTEmdGhpcy5jb250ZW50PygwLG8uc3RyaW5nRnJvbUNvZGVQb2ludCkoMjA5NzE1MSZ0aGlzLmNvbnRlbnQpOiIifSx0LnByb3RvdHlwZS5nZXRDb2RlPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuaXNDb21iaW5lZCgpP3RoaXMuY29tYmluZWREYXRhLmNoYXJDb2RlQXQodGhpcy5jb21iaW5lZERhdGEubGVuZ3RoLTEpOjIwOTcxNTEmdGhpcy5jb250ZW50fSx0LnByb3RvdHlwZS5zZXRGcm9tQ2hhckRhdGE9ZnVuY3Rpb24oZSl7dGhpcy5mZz1lW3MuQ0hBUl9EQVRBX0FUVFJfSU5ERVhdLHRoaXMuYmc9MDt2YXIgdD0hMTtpZihlW3MuQ0hBUl9EQVRBX0NIQVJfSU5ERVhdLmxlbmd0aD4yKXQ9ITA7ZWxzZSBpZigyPT09ZVtzLkNIQVJfREFUQV9DSEFSX0lOREVYXS5sZW5ndGgpe3ZhciByPWVbcy5DSEFSX0RBVEFfQ0hBUl9JTkRFWF0uY2hhckNvZGVBdCgwKTtpZig1NTI5Njw9ciYmcjw9NTYzMTkpe3ZhciBpPWVbcy5DSEFSX0RBVEFfQ0hBUl9JTkRFWF0uY2hhckNvZGVBdCgxKTs1NjMyMDw9aSYmaTw9NTczNDM/dGhpcy5jb250ZW50PTEwMjQqKHItNTUyOTYpK2ktNTYzMjArNjU1MzZ8ZVtzLkNIQVJfREFUQV9XSURUSF9JTkRFWF08PDIyOnQ9ITB9ZWxzZSB0PSEwfWVsc2UgdGhpcy5jb250ZW50PWVbcy5DSEFSX0RBVEFfQ0hBUl9JTkRFWF0uY2hhckNvZGVBdCgwKXxlW3MuQ0hBUl9EQVRBX1dJRFRIX0lOREVYXTw8MjI7dCYmKHRoaXMuY29tYmluZWREYXRhPWVbcy5DSEFSX0RBVEFfQ0hBUl9JTkRFWF0sdGhpcy5jb250ZW50PTIwOTcxNTJ8ZVtzLkNIQVJfREFUQV9XSURUSF9JTkRFWF08PDIyKX0sdC5wcm90b3R5cGUuZ2V0QXNDaGFyRGF0YT1mdW5jdGlvbigpe3JldHVyblt0aGlzLmZnLHRoaXMuZ2V0Q2hhcnMoKSx0aGlzLmdldFdpZHRoKCksdGhpcy5nZXRDb2RlKCldfSx0fShhLkF0dHJpYnV0ZURhdGEpO3QuQ2VsbERhdGE9Y30sNjQzOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuV0hJVEVTUEFDRV9DRUxMX0NPREU9dC5XSElURVNQQUNFX0NFTExfV0lEVEg9dC5XSElURVNQQUNFX0NFTExfQ0hBUj10Lk5VTExfQ0VMTF9DT0RFPXQuTlVMTF9DRUxMX1dJRFRIPXQuTlVMTF9DRUxMX0NIQVI9dC5DSEFSX0RBVEFfQ09ERV9JTkRFWD10LkNIQVJfREFUQV9XSURUSF9JTkRFWD10LkNIQVJfREFUQV9DSEFSX0lOREVYPXQuQ0hBUl9EQVRBX0FUVFJfSU5ERVg9dC5ERUZBVUxUX0FUVFI9dC5ERUZBVUxUX0NPTE9SPXZvaWQgMCx0LkRFRkFVTFRfQ09MT1I9MjU2LHQuREVGQVVMVF9BVFRSPTI1Nnx0LkRFRkFVTFRfQ09MT1I8PDksdC5DSEFSX0RBVEFfQVRUUl9JTkRFWD0wLHQuQ0hBUl9EQVRBX0NIQVJfSU5ERVg9MSx0LkNIQVJfREFUQV9XSURUSF9JTkRFWD0yLHQuQ0hBUl9EQVRBX0NPREVfSU5ERVg9Myx0Lk5VTExfQ0VMTF9DSEFSPSIiLHQuTlVMTF9DRUxMX1dJRFRIPTEsdC5OVUxMX0NFTExfQ09ERT0wLHQuV0hJVEVTUEFDRV9DRUxMX0NIQVI9IiAiLHQuV0hJVEVTUEFDRV9DRUxMX1dJRFRIPTEsdC5XSElURVNQQUNFX0NFTExfQ09ERT0zMn0sNDg2MzpmdW5jdGlvbihlLHQscil7dmFyIGksbj10aGlzJiZ0aGlzLl9fZXh0ZW5kc3x8KGk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gaT1PYmplY3Quc2V0UHJvdG90eXBlT2Z8fHtfX3Byb3RvX186W119aW5zdGFuY2VvZiBBcnJheSYmZnVuY3Rpb24oZSx0KXtlLl9fcHJvdG9fXz10fXx8ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHIgaW4gdClPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodCxyKSYmKGVbcl09dFtyXSl9LGkoZSx0KX0sZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgdCYmbnVsbCE9PXQpdGhyb3cgbmV3IFR5cGVFcnJvcigiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSAiK1N0cmluZyh0KSsiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGwiKTtmdW5jdGlvbiByKCl7dGhpcy5jb25zdHJ1Y3Rvcj1lfWkoZSx0KSxlLnByb3RvdHlwZT1udWxsPT09dD9PYmplY3QuY3JlYXRlKHQpOihyLnByb3RvdHlwZT10LnByb3RvdHlwZSxuZXcgcil9KTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5NYXJrZXI9dm9pZCAwO3ZhciBvPXIoODQ2MCkscz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHIpe3ZhciBpPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gaS5saW5lPXIsaS5faWQ9dC5fbmV4dElkKyssaS5pc0Rpc3Bvc2VkPSExLGkuX29uRGlzcG9zZT1uZXcgby5FdmVudEVtaXR0ZXIsaX1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwiaWQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5faWR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkRpc3Bvc2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25EaXNwb3NlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLHQucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt0aGlzLmlzRGlzcG9zZWR8fCh0aGlzLmlzRGlzcG9zZWQ9ITAsdGhpcy5saW5lPS0xLHRoaXMuX29uRGlzcG9zZS5maXJlKCksZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpKX0sdC5fbmV4dElkPTEsdH0ocig4NDQpLkRpc3Bvc2FibGUpO3QuTWFya2VyPXN9LDcxMTY6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5ERUZBVUxUX0NIQVJTRVQ9dC5DSEFSU0VUUz12b2lkIDAsdC5DSEFSU0VUUz17fSx0LkRFRkFVTFRfQ0hBUlNFVD10LkNIQVJTRVRTLkIsdC5DSEFSU0VUU1swXT17ImAiOiLil4YiLGE6IuKWkiIsYjoi4pCJIixjOiLikIwiLGQ6IuKQjSIsZToi4pCKIixmOiLCsCIsZzoiwrEiLGg6IuKQpCIsaToi4pCLIixqOiLilJgiLGs6IuKUkCIsbDoi4pSMIixtOiLilJQiLG46IuKUvCIsbzoi4o66IixwOiLijrsiLHE6IuKUgCIscjoi4o68IixzOiLijr0iLHQ6IuKUnCIsdToi4pSkIix2OiLilLQiLHc6IuKUrCIseDoi4pSCIix5OiLiiaQiLHo6IuKJpSIsInsiOiLPgCIsInwiOiLiiaAiLCJ9IjoiwqMiLCJ+IjoiwrcifSx0LkNIQVJTRVRTLkE9eyIjIjoiwqMifSx0LkNIQVJTRVRTLkI9dm9pZCAwLHQuQ0hBUlNFVFNbNF09eyIjIjoiwqMiLCJAIjoiwr4iLCJbIjoiaWoiLCJcXCI6IsK9IiwiXSI6InwiLCJ7IjoiwqgiLCJ8IjoiZiIsIn0iOiLCvCIsIn4iOiLCtCJ9LHQuQ0hBUlNFVFMuQz10LkNIQVJTRVRTWzVdPXsiWyI6IsOEIiwiXFwiOiLDliIsIl0iOiLDhSIsIl4iOiLDnCIsImAiOiLDqSIsInsiOiLDpCIsInwiOiLDtiIsIn0iOiLDpSIsIn4iOiLDvCJ9LHQuQ0hBUlNFVFMuUj17IiMiOiLCoyIsIkAiOiLDoCIsIlsiOiLCsCIsIlxcIjoiw6ciLCJdIjoiwqciLCJ7Ijoiw6kiLCJ8Ijoiw7kiLCJ9Ijoiw6giLCJ+IjoiwqgifSx0LkNIQVJTRVRTLlE9eyJAIjoiw6AiLCJbIjoiw6IiLCJcXCI6IsOnIiwiXSI6IsOqIiwiXiI6IsOuIiwiYCI6IsO0IiwieyI6IsOpIiwifCI6IsO5IiwifSI6IsOoIiwifiI6IsO7In0sdC5DSEFSU0VUUy5LPXsiQCI6IsKnIiwiWyI6IsOEIiwiXFwiOiLDliIsIl0iOiLDnCIsInsiOiLDpCIsInwiOiLDtiIsIn0iOiLDvCIsIn4iOiLDnyJ9LHQuQ0hBUlNFVFMuWT17IiMiOiLCoyIsIkAiOiLCpyIsIlsiOiLCsCIsIlxcIjoiw6ciLCJdIjoiw6kiLCJgIjoiw7kiLCJ7Ijoiw6AiLCJ8Ijoiw7IiLCJ9Ijoiw6giLCJ+Ijoiw6wifSx0LkNIQVJTRVRTLkU9dC5DSEFSU0VUU1s2XT17IkAiOiLDhCIsIlsiOiLDhiIsIlxcIjoiw5giLCJdIjoiw4UiLCJeIjoiw5wiLCJgIjoiw6QiLCJ7Ijoiw6YiLCJ8Ijoiw7giLCJ9Ijoiw6UiLCJ+Ijoiw7wifSx0LkNIQVJTRVRTLlo9eyIjIjoiwqMiLCJAIjoiwqciLCJbIjoiwqEiLCJcXCI6IsORIiwiXSI6IsK/IiwieyI6IsKwIiwifCI6IsOxIiwifSI6IsOnIn0sdC5DSEFSU0VUUy5IPXQuQ0hBUlNFVFNbN109eyJAIjoiw4kiLCJbIjoiw4QiLCJcXCI6IsOWIiwiXSI6IsOFIiwiXiI6IsOcIiwiYCI6IsOpIiwieyI6IsOkIiwifCI6IsO2IiwifSI6IsOlIiwifiI6IsO8In0sdC5DSEFSU0VUU1siPSJdPXsiIyI6IsO5IiwiQCI6IsOgIiwiWyI6IsOpIiwiXFwiOiLDpyIsIl0iOiLDqiIsIl4iOiLDriIsXzoiw6giLCJgIjoiw7QiLCJ7Ijoiw6QiLCJ8Ijoiw7YiLCJ9Ijoiw7wiLCJ+Ijoiw7sifX0sMjU4NDooZSx0KT0+e3ZhciByLGk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQzE9dC5DMD12b2lkIDAsKGk9dC5DMHx8KHQuQzA9e30pKS5OVUw9IlwwIixpLlNPSD0iASIsaS5TVFg9IgIiLGkuRVRYPSIDIixpLkVPVD0iBCIsaS5FTlE9IgUiLGkuQUNLPSIGIixpLkJFTD0iByIsaS5CUz0iXGIiLGkuSFQ9Ilx0IixpLkxGPSJcbiIsaS5WVD0iXHYiLGkuRkY9IlxmIixpLkNSPSJcciIsaS5TTz0iDiIsaS5TST0iDyIsaS5ETEU9IhAiLGkuREMxPSIRIixpLkRDMj0iEiIsaS5EQzM9IhMiLGkuREM0PSIUIixpLk5BSz0iFSIsaS5TWU49IhYiLGkuRVRCPSIXIixpLkNBTj0iGCIsaS5FTT0iGSIsaS5TVUI9IhoiLGkuRVNDPSIbIixpLkZTPSIcIixpLkdTPSIdIixpLlJTPSIeIixpLlVTPSIfIixpLlNQPSIgIixpLkRFTD0ifyIsKHI9dC5DMXx8KHQuQzE9e30pKS5QQUQ9IsKAIixyLkhPUD0iwoEiLHIuQlBIPSLCgiIsci5OQkg9IsKDIixyLklORD0iwoQiLHIuTkVMPSLChSIsci5TU0E9IsKGIixyLkVTQT0iwociLHIuSFRTPSLCiCIsci5IVEo9IsKJIixyLlZUUz0iwooiLHIuUExEPSLCiyIsci5QTFU9IsKMIixyLlJJPSLCjSIsci5TUzI9IsKOIixyLlNTMz0iwo8iLHIuRENTPSLCkCIsci5QVTE9IsKRIixyLlBVMj0iwpIiLHIuU1RTPSLCkyIsci5DQ0g9IsKUIixyLk1XPSLClSIsci5TUEE9IsKWIixyLkVQQT0iwpciLHIuU09TPSLCmCIsci5TR0NJPSLCmSIsci5TQ0k9IsKaIixyLkNTST0iwpsiLHIuU1Q9IsKcIixyLk9TQz0iwp0iLHIuUE09IsKeIixyLkFQQz0iwp8ifSw3Mzk5OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5ldmFsdWF0ZUtleWJvYXJkRXZlbnQ9dm9pZCAwO3ZhciBpPXIoMjU4NCksbj17NDg6WyIwIiwiKSJdLDQ5OlsiMSIsIiEiXSw1MDpbIjIiLCJAIl0sNTE6WyIzIiwiIyJdLDUyOlsiNCIsIiQiXSw1MzpbIjUiLCIlIl0sNTQ6WyI2IiwiXiJdLDU1OlsiNyIsIiYiXSw1NjpbIjgiLCIqIl0sNTc6WyI5IiwiKCJdLDE4NjpbIjsiLCI6Il0sMTg3OlsiPSIsIisiXSwxODg6WyIsIiwiPCJdLDE4OTpbIi0iLCJfIl0sMTkwOlsiLiIsIj4iXSwxOTE6WyIvIiwiPyJdLDE5MjpbImAiLCJ+Il0sMjE5OlsiWyIsInsiXSwyMjA6WyJcXCIsInwiXSwyMjE6WyJdIiwifSJdLDIyMjpbIiciLCciJ119O3QuZXZhbHVhdGVLZXlib2FyZEV2ZW50PWZ1bmN0aW9uKGUsdCxyLG8pe3ZhciBzPXt0eXBlOjAsY2FuY2VsOiExLGtleTp2b2lkIDB9LGE9KGUuc2hpZnRLZXk/MTowKXwoZS5hbHRLZXk/MjowKXwoZS5jdHJsS2V5PzQ6MCl8KGUubWV0YUtleT84OjApO3N3aXRjaChlLmtleUNvZGUpe2Nhc2UgMDoiVUlLZXlJbnB1dFVwQXJyb3ciPT09ZS5rZXk/cy5rZXk9dD9pLkMwLkVTQysiT0EiOmkuQzAuRVNDKyJbQSI6IlVJS2V5SW5wdXRMZWZ0QXJyb3ciPT09ZS5rZXk/cy5rZXk9dD9pLkMwLkVTQysiT0QiOmkuQzAuRVNDKyJbRCI6IlVJS2V5SW5wdXRSaWdodEFycm93Ij09PWUua2V5P3Mua2V5PXQ/aS5DMC5FU0MrIk9DIjppLkMwLkVTQysiW0MiOiJVSUtleUlucHV0RG93bkFycm93Ij09PWUua2V5JiYocy5rZXk9dD9pLkMwLkVTQysiT0IiOmkuQzAuRVNDKyJbQiIpO2JyZWFrO2Nhc2UgODppZihlLnNoaWZ0S2V5KXtzLmtleT1pLkMwLkJTO2JyZWFrfWlmKGUuYWx0S2V5KXtzLmtleT1pLkMwLkVTQytpLkMwLkRFTDticmVha31zLmtleT1pLkMwLkRFTDticmVhaztjYXNlIDk6aWYoZS5zaGlmdEtleSl7cy5rZXk9aS5DMC5FU0MrIltaIjticmVha31zLmtleT1pLkMwLkhULHMuY2FuY2VsPSEwO2JyZWFrO2Nhc2UgMTM6cy5rZXk9ZS5hbHRLZXk/aS5DMC5FU0MraS5DMC5DUjppLkMwLkNSLHMuY2FuY2VsPSEwO2JyZWFrO2Nhc2UgMjc6cy5rZXk9aS5DMC5FU0MsZS5hbHRLZXkmJihzLmtleT1pLkMwLkVTQytpLkMwLkVTQykscy5jYW5jZWw9ITA7YnJlYWs7Y2FzZSAzNzppZihlLm1ldGFLZXkpYnJlYWs7YT8ocy5rZXk9aS5DMC5FU0MrIlsxOyIrKGErMSkrIkQiLHMua2V5PT09aS5DMC5FU0MrIlsxOzNEIiYmKHMua2V5PWkuQzAuRVNDKyhyPyJiIjoiWzE7NUQiKSkpOnMua2V5PXQ/aS5DMC5FU0MrIk9EIjppLkMwLkVTQysiW0QiO2JyZWFrO2Nhc2UgMzk6aWYoZS5tZXRhS2V5KWJyZWFrO2E/KHMua2V5PWkuQzAuRVNDKyJbMTsiKyhhKzEpKyJDIixzLmtleT09PWkuQzAuRVNDKyJbMTszQyImJihzLmtleT1pLkMwLkVTQysocj8iZiI6IlsxOzVDIikpKTpzLmtleT10P2kuQzAuRVNDKyJPQyI6aS5DMC5FU0MrIltDIjticmVhaztjYXNlIDM4OmlmKGUubWV0YUtleSlicmVhazthPyhzLmtleT1pLkMwLkVTQysiWzE7IisoYSsxKSsiQSIscnx8cy5rZXkhPT1pLkMwLkVTQysiWzE7M0EifHwocy5rZXk9aS5DMC5FU0MrIlsxOzVBIikpOnMua2V5PXQ/aS5DMC5FU0MrIk9BIjppLkMwLkVTQysiW0EiO2JyZWFrO2Nhc2UgNDA6aWYoZS5tZXRhS2V5KWJyZWFrO2E/KHMua2V5PWkuQzAuRVNDKyJbMTsiKyhhKzEpKyJCIixyfHxzLmtleSE9PWkuQzAuRVNDKyJbMTszQiJ8fChzLmtleT1pLkMwLkVTQysiWzE7NUIiKSk6cy5rZXk9dD9pLkMwLkVTQysiT0IiOmkuQzAuRVNDKyJbQiI7YnJlYWs7Y2FzZSA0NTplLnNoaWZ0S2V5fHxlLmN0cmxLZXl8fChzLmtleT1pLkMwLkVTQysiWzJ+Iik7YnJlYWs7Y2FzZSA0NjpzLmtleT1hP2kuQzAuRVNDKyJbMzsiKyhhKzEpKyJ+IjppLkMwLkVTQysiWzN+IjticmVhaztjYXNlIDM2OnMua2V5PWE/aS5DMC5FU0MrIlsxOyIrKGErMSkrIkgiOnQ/aS5DMC5FU0MrIk9IIjppLkMwLkVTQysiW0giO2JyZWFrO2Nhc2UgMzU6cy5rZXk9YT9pLkMwLkVTQysiWzE7IisoYSsxKSsiRiI6dD9pLkMwLkVTQysiT0YiOmkuQzAuRVNDKyJbRiI7YnJlYWs7Y2FzZSAzMzplLnNoaWZ0S2V5P3MudHlwZT0yOnMua2V5PWkuQzAuRVNDKyJbNX4iO2JyZWFrO2Nhc2UgMzQ6ZS5zaGlmdEtleT9zLnR5cGU9MzpzLmtleT1pLkMwLkVTQysiWzZ+IjticmVhaztjYXNlIDExMjpzLmtleT1hP2kuQzAuRVNDKyJbMTsiKyhhKzEpKyJQIjppLkMwLkVTQysiT1AiO2JyZWFrO2Nhc2UgMTEzOnMua2V5PWE/aS5DMC5FU0MrIlsxOyIrKGErMSkrIlEiOmkuQzAuRVNDKyJPUSI7YnJlYWs7Y2FzZSAxMTQ6cy5rZXk9YT9pLkMwLkVTQysiWzE7IisoYSsxKSsiUiI6aS5DMC5FU0MrIk9SIjticmVhaztjYXNlIDExNTpzLmtleT1hP2kuQzAuRVNDKyJbMTsiKyhhKzEpKyJTIjppLkMwLkVTQysiT1MiO2JyZWFrO2Nhc2UgMTE2OnMua2V5PWE/aS5DMC5FU0MrIlsxNTsiKyhhKzEpKyJ+IjppLkMwLkVTQysiWzE1fiI7YnJlYWs7Y2FzZSAxMTc6cy5rZXk9YT9pLkMwLkVTQysiWzE3OyIrKGErMSkrIn4iOmkuQzAuRVNDKyJbMTd+IjticmVhaztjYXNlIDExODpzLmtleT1hP2kuQzAuRVNDKyJbMTg7IisoYSsxKSsifiI6aS5DMC5FU0MrIlsxOH4iO2JyZWFrO2Nhc2UgMTE5OnMua2V5PWE/aS5DMC5FU0MrIlsxOTsiKyhhKzEpKyJ+IjppLkMwLkVTQysiWzE5fiI7YnJlYWs7Y2FzZSAxMjA6cy5rZXk9YT9pLkMwLkVTQysiWzIwOyIrKGErMSkrIn4iOmkuQzAuRVNDKyJbMjB+IjticmVhaztjYXNlIDEyMTpzLmtleT1hP2kuQzAuRVNDKyJbMjE7IisoYSsxKSsifiI6aS5DMC5FU0MrIlsyMX4iO2JyZWFrO2Nhc2UgMTIyOnMua2V5PWE/aS5DMC5FU0MrIlsyMzsiKyhhKzEpKyJ+IjppLkMwLkVTQysiWzIzfiI7YnJlYWs7Y2FzZSAxMjM6cy5rZXk9YT9pLkMwLkVTQysiWzI0OyIrKGErMSkrIn4iOmkuQzAuRVNDKyJbMjR+IjticmVhaztkZWZhdWx0OmlmKCFlLmN0cmxLZXl8fGUuc2hpZnRLZXl8fGUuYWx0S2V5fHxlLm1ldGFLZXkpaWYociYmIW98fCFlLmFsdEtleXx8ZS5tZXRhS2V5KSFyfHxlLmFsdEtleXx8ZS5jdHJsS2V5fHxlLnNoaWZ0S2V5fHwhZS5tZXRhS2V5P2Uua2V5JiYhZS5jdHJsS2V5JiYhZS5hbHRLZXkmJiFlLm1ldGFLZXkmJmUua2V5Q29kZT49NDgmJjE9PT1lLmtleS5sZW5ndGg/cy5rZXk9ZS5rZXk6ZS5rZXkmJmUuY3RybEtleSYmIl8iPT09ZS5rZXkmJihzLmtleT1pLkMwLlVTKTo2NT09PWUua2V5Q29kZSYmKHMudHlwZT0xKTtlbHNle3ZhciBjPW5bZS5rZXlDb2RlXSxsPW51bGw9PWM/dm9pZCAwOmNbZS5zaGlmdEtleT8xOjBdO2lmKGwpcy5rZXk9aS5DMC5FU0MrbDtlbHNlIGlmKGUua2V5Q29kZT49NjUmJmUua2V5Q29kZTw9OTApe3ZhciB1PWUuY3RybEtleT9lLmtleUNvZGUtNjQ6ZS5rZXlDb2RlKzMyO3Mua2V5PWkuQzAuRVNDK1N0cmluZy5mcm9tQ2hhckNvZGUodSl9fWVsc2UgZS5rZXlDb2RlPj02NSYmZS5rZXlDb2RlPD05MD9zLmtleT1TdHJpbmcuZnJvbUNoYXJDb2RlKGUua2V5Q29kZS02NCk6MzI9PT1lLmtleUNvZGU/cy5rZXk9aS5DMC5OVUw6ZS5rZXlDb2RlPj01MSYmZS5rZXlDb2RlPD01NT9zLmtleT1TdHJpbmcuZnJvbUNoYXJDb2RlKGUua2V5Q29kZS01MSsyNyk6NTY9PT1lLmtleUNvZGU/cy5rZXk9aS5DMC5ERUw6MjE5PT09ZS5rZXlDb2RlP3Mua2V5PWkuQzAuRVNDOjIyMD09PWUua2V5Q29kZT9zLmtleT1pLkMwLkZTOjIyMT09PWUua2V5Q29kZSYmKHMua2V5PWkuQzAuR1MpfXJldHVybiBzfX0sNDgyOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuVXRmOFRvVXRmMzI9dC5TdHJpbmdUb1V0ZjMyPXQudXRmMzJUb1N0cmluZz10LnN0cmluZ0Zyb21Db2RlUG9pbnQ9dm9pZCAwLHQuc3RyaW5nRnJvbUNvZGVQb2ludD1mdW5jdGlvbihlKXtyZXR1cm4gZT42NTUzNT8oZS09NjU1MzYsU3RyaW5nLmZyb21DaGFyQ29kZSg1NTI5NisoZT4+MTApKStTdHJpbmcuZnJvbUNoYXJDb2RlKGUlMTAyNCs1NjMyMCkpOlN0cmluZy5mcm9tQ2hhckNvZGUoZSl9LHQudXRmMzJUb1N0cmluZz1mdW5jdGlvbihlLHQscil7dm9pZCAwPT09dCYmKHQ9MCksdm9pZCAwPT09ciYmKHI9ZS5sZW5ndGgpO2Zvcih2YXIgaT0iIixuPXQ7bjxyOysrbil7dmFyIG89ZVtuXTtvPjY1NTM1PyhvLT02NTUzNixpKz1TdHJpbmcuZnJvbUNoYXJDb2RlKDU1Mjk2KyhvPj4xMCkpK1N0cmluZy5mcm9tQ2hhckNvZGUobyUxMDI0KzU2MzIwKSk6aSs9U3RyaW5nLmZyb21DaGFyQ29kZShvKX1yZXR1cm4gaX07dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKCl7dGhpcy5faW50ZXJpbT0wfXJldHVybiBlLnByb3RvdHlwZS5jbGVhcj1mdW5jdGlvbigpe3RoaXMuX2ludGVyaW09MH0sZS5wcm90b3R5cGUuZGVjb2RlPWZ1bmN0aW9uKGUsdCl7dmFyIHI9ZS5sZW5ndGg7aWYoIXIpcmV0dXJuIDA7dmFyIGk9MCxuPTA7dGhpcy5faW50ZXJpbSYmKDU2MzIwPD0oYT1lLmNoYXJDb2RlQXQobisrKSkmJmE8PTU3MzQzP3RbaSsrXT0xMDI0Kih0aGlzLl9pbnRlcmltLTU1Mjk2KSthLTU2MzIwKzY1NTM2Oih0W2krK109dGhpcy5faW50ZXJpbSx0W2krK109YSksdGhpcy5faW50ZXJpbT0wKTtmb3IodmFyIG89bjtvPHI7KytvKXt2YXIgcz1lLmNoYXJDb2RlQXQobyk7aWYoNTUyOTY8PXMmJnM8PTU2MzE5KXtpZigrK28+PXIpcmV0dXJuIHRoaXMuX2ludGVyaW09cyxpO3ZhciBhOzU2MzIwPD0oYT1lLmNoYXJDb2RlQXQobykpJiZhPD01NzM0Mz90W2krK109MTAyNCoocy01NTI5NikrYS01NjMyMCs2NTUzNjoodFtpKytdPXMsdFtpKytdPWEpfWVsc2UgNjUyNzkhPT1zJiYodFtpKytdPXMpfXJldHVybiBpfSxlfSgpO3QuU3RyaW5nVG9VdGYzMj1yO3ZhciBpPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuaW50ZXJpbT1uZXcgVWludDhBcnJheSgzKX1yZXR1cm4gZS5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXt0aGlzLmludGVyaW0uZmlsbCgwKX0sZS5wcm90b3R5cGUuZGVjb2RlPWZ1bmN0aW9uKGUsdCl7dmFyIHI9ZS5sZW5ndGg7aWYoIXIpcmV0dXJuIDA7dmFyIGksbixvLHMsYT0wLGM9MCxsPTA7aWYodGhpcy5pbnRlcmltWzBdKXt2YXIgdT0hMSxoPXRoaXMuaW50ZXJpbVswXTtoJj0xOTI9PSgyMjQmaCk/MzE6MjI0PT0oMjQwJmgpPzE1Ojc7Zm9yKHZhciBmPTAsXz12b2lkIDA7KF89NjMmdGhpcy5pbnRlcmltWysrZl0pJiZmPDQ7KWg8PD02LGh8PV87Zm9yKHZhciBkPTE5Mj09KDIyNCZ0aGlzLmludGVyaW1bMF0pPzI6MjI0PT0oMjQwJnRoaXMuaW50ZXJpbVswXSk/Mzo0LHA9ZC1mO2w8cDspe2lmKGw+PXIpcmV0dXJuIDA7aWYoMTI4IT0oMTkyJihfPWVbbCsrXSkpKXtsLS0sdT0hMDticmVha310aGlzLmludGVyaW1bZisrXT1fLGg8PD02LGh8PTYzJl99dXx8KDI9PT1kP2g8MTI4P2wtLTp0W2ErK109aDozPT09ZD9oPDIwNDh8fGg+PTU1Mjk2JiZoPD01NzM0M3x8NjUyNzk9PT1ofHwodFthKytdPWgpOmg8NjU1MzZ8fGg+MTExNDExMXx8KHRbYSsrXT1oKSksdGhpcy5pbnRlcmltLmZpbGwoMCl9Zm9yKHZhciB2PXItNCxnPWw7ZzxyOyl7Zm9yKDshKCEoZzx2KXx8MTI4JihpPWVbZ10pfHwxMjgmKG49ZVtnKzFdKXx8MTI4JihvPWVbZysyXSl8fDEyOCYocz1lW2crM10pKTspdFthKytdPWksdFthKytdPW4sdFthKytdPW8sdFthKytdPXMsZys9NDtpZigoaT1lW2crK10pPDEyOCl0W2ErK109aTtlbHNlIGlmKDE5Mj09KDIyNCZpKSl7aWYoZz49cilyZXR1cm4gdGhpcy5pbnRlcmltWzBdPWksYTtpZigxMjghPSgxOTImKG49ZVtnKytdKSkpe2ctLTtjb250aW51ZX1pZigoYz0oMzEmaSk8PDZ8NjMmbik8MTI4KXtnLS07Y29udGludWV9dFthKytdPWN9ZWxzZSBpZigyMjQ9PSgyNDAmaSkpe2lmKGc+PXIpcmV0dXJuIHRoaXMuaW50ZXJpbVswXT1pLGE7aWYoMTI4IT0oMTkyJihuPWVbZysrXSkpKXtnLS07Y29udGludWV9aWYoZz49cilyZXR1cm4gdGhpcy5pbnRlcmltWzBdPWksdGhpcy5pbnRlcmltWzFdPW4sYTtpZigxMjghPSgxOTImKG89ZVtnKytdKSkpe2ctLTtjb250aW51ZX1pZigoYz0oMTUmaSk8PDEyfCg2MyZuKTw8Nnw2MyZvKTwyMDQ4fHxjPj01NTI5NiYmYzw9NTczNDN8fDY1Mjc5PT09Yyljb250aW51ZTt0W2ErK109Y31lbHNlIGlmKDI0MD09KDI0OCZpKSl7aWYoZz49cilyZXR1cm4gdGhpcy5pbnRlcmltWzBdPWksYTtpZigxMjghPSgxOTImKG49ZVtnKytdKSkpe2ctLTtjb250aW51ZX1pZihnPj1yKXJldHVybiB0aGlzLmludGVyaW1bMF09aSx0aGlzLmludGVyaW1bMV09bixhO2lmKDEyOCE9KDE5MiYobz1lW2crK10pKSl7Zy0tO2NvbnRpbnVlfWlmKGc+PXIpcmV0dXJuIHRoaXMuaW50ZXJpbVswXT1pLHRoaXMuaW50ZXJpbVsxXT1uLHRoaXMuaW50ZXJpbVsyXT1vLGE7aWYoMTI4IT0oMTkyJihzPWVbZysrXSkpKXtnLS07Y29udGludWV9aWYoKGM9KDcmaSk8PDE4fCg2MyZuKTw8MTJ8KDYzJm8pPDw2fDYzJnMpPDY1NTM2fHxjPjExMTQxMTEpY29udGludWU7dFthKytdPWN9fXJldHVybiBhfSxlfSgpO3QuVXRmOFRvVXRmMzI9aX0sMjI1OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Vbmljb2RlVjY9dm9pZCAwO3ZhciBpLG49cig4MjczKSxvPVtbNzY4LDg3OV0sWzExNTUsMTE1OF0sWzExNjAsMTE2MV0sWzE0MjUsMTQ2OV0sWzE0NzEsMTQ3MV0sWzE0NzMsMTQ3NF0sWzE0NzYsMTQ3N10sWzE0NzksMTQ3OV0sWzE1MzYsMTUzOV0sWzE1NTIsMTU1N10sWzE2MTEsMTYzMF0sWzE2NDgsMTY0OF0sWzE3NTAsMTc2NF0sWzE3NjcsMTc2OF0sWzE3NzAsMTc3M10sWzE4MDcsMTgwN10sWzE4MDksMTgwOV0sWzE4NDAsMTg2Nl0sWzE5NTgsMTk2OF0sWzIwMjcsMjAzNV0sWzIzMDUsMjMwNl0sWzIzNjQsMjM2NF0sWzIzNjksMjM3Nl0sWzIzODEsMjM4MV0sWzIzODUsMjM4OF0sWzI0MDIsMjQwM10sWzI0MzMsMjQzM10sWzI0OTIsMjQ5Ml0sWzI0OTcsMjUwMF0sWzI1MDksMjUwOV0sWzI1MzAsMjUzMV0sWzI1NjEsMjU2Ml0sWzI2MjAsMjYyMF0sWzI2MjUsMjYyNl0sWzI2MzEsMjYzMl0sWzI2MzUsMjYzN10sWzI2NzIsMjY3M10sWzI2ODksMjY5MF0sWzI3NDgsMjc0OF0sWzI3NTMsMjc1N10sWzI3NTksMjc2MF0sWzI3NjUsMjc2NV0sWzI3ODYsMjc4N10sWzI4MTcsMjgxN10sWzI4NzYsMjg3Nl0sWzI4NzksMjg3OV0sWzI4ODEsMjg4M10sWzI4OTMsMjg5M10sWzI5MDIsMjkwMl0sWzI5NDYsMjk0Nl0sWzMwMDgsMzAwOF0sWzMwMjEsMzAyMV0sWzMxMzQsMzEzNl0sWzMxNDIsMzE0NF0sWzMxNDYsMzE0OV0sWzMxNTcsMzE1OF0sWzMyNjAsMzI2MF0sWzMyNjMsMzI2M10sWzMyNzAsMzI3MF0sWzMyNzYsMzI3N10sWzMyOTgsMzI5OV0sWzMzOTMsMzM5NV0sWzM0MDUsMzQwNV0sWzM1MzAsMzUzMF0sWzM1MzgsMzU0MF0sWzM1NDIsMzU0Ml0sWzM2MzMsMzYzM10sWzM2MzYsMzY0Ml0sWzM2NTUsMzY2Ml0sWzM3NjEsMzc2MV0sWzM3NjQsMzc2OV0sWzM3NzEsMzc3Ml0sWzM3ODQsMzc4OV0sWzM4NjQsMzg2NV0sWzM4OTMsMzg5M10sWzM4OTUsMzg5NV0sWzM4OTcsMzg5N10sWzM5NTMsMzk2Nl0sWzM5NjgsMzk3Ml0sWzM5NzQsMzk3NV0sWzM5ODQsMzk5MV0sWzM5OTMsNDAyOF0sWzQwMzgsNDAzOF0sWzQxNDEsNDE0NF0sWzQxNDYsNDE0Nl0sWzQxNTAsNDE1MV0sWzQxNTMsNDE1M10sWzQxODQsNDE4NV0sWzQ0NDgsNDYwN10sWzQ5NTksNDk1OV0sWzU5MDYsNTkwOF0sWzU5MzgsNTk0MF0sWzU5NzAsNTk3MV0sWzYwMDIsNjAwM10sWzYwNjgsNjA2OV0sWzYwNzEsNjA3N10sWzYwODYsNjA4Nl0sWzYwODksNjA5OV0sWzYxMDksNjEwOV0sWzYxNTUsNjE1N10sWzYzMTMsNjMxM10sWzY0MzIsNjQzNF0sWzY0MzksNjQ0MF0sWzY0NTAsNjQ1MF0sWzY0NTcsNjQ1OV0sWzY2NzksNjY4MF0sWzY5MTIsNjkxNV0sWzY5NjQsNjk2NF0sWzY5NjYsNjk3MF0sWzY5NzIsNjk3Ml0sWzY5NzgsNjk3OF0sWzcwMTksNzAyN10sWzc2MTYsNzYyNl0sWzc2NzgsNzY3OV0sWzgyMDMsODIwN10sWzgyMzQsODIzOF0sWzgyODgsODI5MV0sWzgyOTgsODMwM10sWzg0MDAsODQzMV0sWzEyMzMwLDEyMzM1XSxbMTI0NDEsMTI0NDJdLFs0MzAxNCw0MzAxNF0sWzQzMDE5LDQzMDE5XSxbNDMwNDUsNDMwNDZdLFs2NDI4Niw2NDI4Nl0sWzY1MDI0LDY1MDM5XSxbNjUwNTYsNjUwNTldLFs2NTI3OSw2NTI3OV0sWzY1NTI5LDY1NTMxXV0scz1bWzY4MDk3LDY4MDk5XSxbNjgxMDEsNjgxMDJdLFs2ODEwOCw2ODExMV0sWzY4MTUyLDY4MTU0XSxbNjgxNTksNjgxNTldLFsxMTkxNDMsMTE5MTQ1XSxbMTE5MTU1LDExOTE3MF0sWzExOTE3MywxMTkxNzldLFsxMTkyMTAsMTE5MjEzXSxbMTE5MzYyLDExOTM2NF0sWzkxNzUwNSw5MTc1MDVdLFs5MTc1MzYsOTE3NjMxXSxbOTE3NzYwLDkxNzk5OV1dLGE9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKCl7aWYodGhpcy52ZXJzaW9uPSI2IiwhaSl7aT1uZXcgVWludDhBcnJheSg2NTUzNiksKDAsbi5maWxsKShpLDEpLGlbMF09MCwoMCxuLmZpbGwpKGksMCwxLDMyKSwoMCxuLmZpbGwpKGksMCwxMjcsMTYwKSwoMCxuLmZpbGwpKGksMiw0MzUyLDQ0NDgpLGlbOTAwMV09MixpWzkwMDJdPTIsKDAsbi5maWxsKShpLDIsMTE5MDQsNDIxOTIpLGlbMTIzNTFdPTEsKDAsbi5maWxsKShpLDIsNDQwMzIsNTUyMDQpLCgwLG4uZmlsbCkoaSwyLDYzNzQ0LDY0MjU2KSwoMCxuLmZpbGwpKGksMiw2NTA0MCw2NTA1MCksKDAsbi5maWxsKShpLDIsNjUwNzIsNjUxMzYpLCgwLG4uZmlsbCkoaSwyLDY1MjgwLDY1Mzc3KSwoMCxuLmZpbGwpKGksMiw2NTUwNCw2NTUxMSk7Zm9yKHZhciBlPTA7ZTxvLmxlbmd0aDsrK2UpKDAsbi5maWxsKShpLDAsb1tlXVswXSxvW2VdWzFdKzEpfX1yZXR1cm4gZS5wcm90b3R5cGUud2N3aWR0aD1mdW5jdGlvbihlKXtyZXR1cm4gZTwzMj8wOmU8MTI3PzE6ZTw2NTUzNj9pW2VdOmZ1bmN0aW9uKGUsdCl7dmFyIHIsaT0wLG49dC5sZW5ndGgtMTtpZihlPHRbMF1bMF18fGU+dFtuXVsxXSlyZXR1cm4hMTtmb3IoO24+PWk7KWlmKGU+dFtyPWkrbj4+MV1bMV0paT1yKzE7ZWxzZXtpZighKGU8dFtyXVswXSkpcmV0dXJuITA7bj1yLTF9cmV0dXJuITF9KGUscyk/MDplPj0xMzEwNzImJmU8PTE5NjYwNXx8ZT49MTk2NjA4JiZlPD0yNjIxNDE/MjoxfSxlfSgpO3QuVW5pY29kZVY2PWF9LDU5ODE6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Xcml0ZUJ1ZmZlcj12b2lkIDA7dmFyIHI9InVuZGVmaW5lZCI9PXR5cGVvZiBxdWV1ZU1pY3JvdGFzaz9mdW5jdGlvbihlKXtQcm9taXNlLnJlc29sdmUoKS50aGVuKGUpfTpxdWV1ZU1pY3JvdGFzayxpPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9hY3Rpb249ZSx0aGlzLl93cml0ZUJ1ZmZlcj1bXSx0aGlzLl9jYWxsYmFja3M9W10sdGhpcy5fcGVuZGluZ0RhdGE9MCx0aGlzLl9idWZmZXJPZmZzZXQ9MCx0aGlzLl9pc1N5bmNXcml0aW5nPSExLHRoaXMuX3N5bmNDYWxscz0wfXJldHVybiBlLnByb3RvdHlwZS53cml0ZVN5bmM9ZnVuY3Rpb24oZSx0KXtpZih2b2lkIDAhPT10JiZ0aGlzLl9zeW5jQ2FsbHM+dCl0aGlzLl9zeW5jQ2FsbHM9MDtlbHNlIGlmKHRoaXMuX3BlbmRpbmdEYXRhKz1lLmxlbmd0aCx0aGlzLl93cml0ZUJ1ZmZlci5wdXNoKGUpLHRoaXMuX2NhbGxiYWNrcy5wdXNoKHZvaWQgMCksdGhpcy5fc3luY0NhbGxzKyssIXRoaXMuX2lzU3luY1dyaXRpbmcpe3ZhciByO2Zvcih0aGlzLl9pc1N5bmNXcml0aW5nPSEwO3I9dGhpcy5fd3JpdGVCdWZmZXIuc2hpZnQoKTspe3RoaXMuX2FjdGlvbihyKTt2YXIgaT10aGlzLl9jYWxsYmFja3Muc2hpZnQoKTtpJiZpKCl9dGhpcy5fcGVuZGluZ0RhdGE9MCx0aGlzLl9idWZmZXJPZmZzZXQ9MjE0NzQ4MzY0Nyx0aGlzLl9pc1N5bmNXcml0aW5nPSExLHRoaXMuX3N5bmNDYWxscz0wfX0sZS5wcm90b3R5cGUud3JpdGU9ZnVuY3Rpb24oZSx0KXt2YXIgcj10aGlzO2lmKHRoaXMuX3BlbmRpbmdEYXRhPjVlNyl0aHJvdyBuZXcgRXJyb3IoIndyaXRlIGRhdGEgZGlzY2FyZGVkLCB1c2UgZmxvdyBjb250cm9sIHRvIGF2b2lkIGxvc2luZyBkYXRhIik7dGhpcy5fd3JpdGVCdWZmZXIubGVuZ3RofHwodGhpcy5fYnVmZmVyT2Zmc2V0PTAsc2V0VGltZW91dCgoZnVuY3Rpb24oKXtyZXR1cm4gci5faW5uZXJXcml0ZSgpfSkpKSx0aGlzLl9wZW5kaW5nRGF0YSs9ZS5sZW5ndGgsdGhpcy5fd3JpdGVCdWZmZXIucHVzaChlKSx0aGlzLl9jYWxsYmFja3MucHVzaCh0KX0sZS5wcm90b3R5cGUuX2lubmVyV3JpdGU9ZnVuY3Rpb24oZSx0KXt2YXIgaT10aGlzO3ZvaWQgMD09PWUmJihlPTApLHZvaWQgMD09PXQmJih0PSEwKTtmb3IodmFyIG49ZXx8RGF0ZS5ub3coKTt0aGlzLl93cml0ZUJ1ZmZlci5sZW5ndGg+dGhpcy5fYnVmZmVyT2Zmc2V0Oyl7dmFyIG89dGhpcy5fd3JpdGVCdWZmZXJbdGhpcy5fYnVmZmVyT2Zmc2V0XSxzPXRoaXMuX2FjdGlvbihvLHQpO2lmKHMpcmV0dXJuIHZvaWQgcy5jYXRjaCgoZnVuY3Rpb24oZSl7cmV0dXJuIHIoKGZ1bmN0aW9uKCl7dGhyb3cgZX0pKSxQcm9taXNlLnJlc29sdmUoITEpfSkpLnRoZW4oKGZ1bmN0aW9uKGUpe3JldHVybiBEYXRlLm5vdygpLW4+PTEyP3NldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIGkuX2lubmVyV3JpdGUoMCxlKX0pKTppLl9pbm5lcldyaXRlKG4sZSl9KSk7dmFyIGE9dGhpcy5fY2FsbGJhY2tzW3RoaXMuX2J1ZmZlck9mZnNldF07aWYoYSYmYSgpLHRoaXMuX2J1ZmZlck9mZnNldCsrLHRoaXMuX3BlbmRpbmdEYXRhLT1vLmxlbmd0aCxEYXRlLm5vdygpLW4+PTEyKWJyZWFrfXRoaXMuX3dyaXRlQnVmZmVyLmxlbmd0aD50aGlzLl9idWZmZXJPZmZzZXQ/KHRoaXMuX2J1ZmZlck9mZnNldD41MCYmKHRoaXMuX3dyaXRlQnVmZmVyPXRoaXMuX3dyaXRlQnVmZmVyLnNsaWNlKHRoaXMuX2J1ZmZlck9mZnNldCksdGhpcy5fY2FsbGJhY2tzPXRoaXMuX2NhbGxiYWNrcy5zbGljZSh0aGlzLl9idWZmZXJPZmZzZXQpLHRoaXMuX2J1ZmZlck9mZnNldD0wKSxzZXRUaW1lb3V0KChmdW5jdGlvbigpe3JldHVybiBpLl9pbm5lcldyaXRlKCl9KSkpOih0aGlzLl93cml0ZUJ1ZmZlci5sZW5ndGg9MCx0aGlzLl9jYWxsYmFja3MubGVuZ3RoPTAsdGhpcy5fcGVuZGluZ0RhdGE9MCx0aGlzLl9idWZmZXJPZmZzZXQ9MCl9LGV9KCk7dC5Xcml0ZUJ1ZmZlcj1pfSw1OTQxOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQudG9SZ2JTdHJpbmc9dC5wYXJzZUNvbG9yPXZvaWQgMDt2YXIgcj0vXihbXGRhLWZdezF9KVwvKFtcZGEtZl17MX0pXC8oW1xkYS1mXXsxfSkkfF4oW1xkYS1mXXsyfSlcLyhbXGRhLWZdezJ9KVwvKFtcZGEtZl17Mn0pJHxeKFtcZGEtZl17M30pXC8oW1xkYS1mXXszfSlcLyhbXGRhLWZdezN9KSR8XihbXGRhLWZdezR9KVwvKFtcZGEtZl17NH0pXC8oW1xkYS1mXXs0fSkkLyxpPS9eW1xkYS1mXSskLztmdW5jdGlvbiBuKGUsdCl7dmFyIHI9ZS50b1N0cmluZygxNiksaT1yLmxlbmd0aDwyPyIwIityOnI7c3dpdGNoKHQpe2Nhc2UgNDpyZXR1cm4gclswXTtjYXNlIDg6cmV0dXJuIGk7Y2FzZSAxMjpyZXR1cm4oaStpKS5zbGljZSgwLDMpO2RlZmF1bHQ6cmV0dXJuIGkraX19dC5wYXJzZUNvbG9yPWZ1bmN0aW9uKGUpe2lmKGUpe3ZhciB0PWUudG9Mb3dlckNhc2UoKTtpZigwPT09dC5pbmRleE9mKCJyZ2I6Iikpe3Q9dC5zbGljZSg0KTt2YXIgbj1yLmV4ZWModCk7aWYobil7dmFyIG89blsxXT8xNTpuWzRdPzI1NTpuWzddPzQwOTU6NjU1MzU7cmV0dXJuW01hdGgucm91bmQocGFyc2VJbnQoblsxXXx8bls0XXx8bls3XXx8blsxMF0sMTYpL28qMjU1KSxNYXRoLnJvdW5kKHBhcnNlSW50KG5bMl18fG5bNV18fG5bOF18fG5bMTFdLDE2KS9vKjI1NSksTWF0aC5yb3VuZChwYXJzZUludChuWzNdfHxuWzZdfHxuWzldfHxuWzEyXSwxNikvbyoyNTUpXX19ZWxzZSBpZigwPT09dC5pbmRleE9mKCIjIikmJih0PXQuc2xpY2UoMSksaS5leGVjKHQpJiZbMyw2LDksMTJdLmluY2x1ZGVzKHQubGVuZ3RoKSkpe2Zvcih2YXIgcz10Lmxlbmd0aC8zLGE9WzAsMCwwXSxjPTA7YzwzOysrYyl7dmFyIGw9cGFyc2VJbnQodC5zbGljZShzKmMscypjK3MpLDE2KTthW2NdPTE9PT1zP2w8PDQ6Mj09PXM/bDozPT09cz9sPj40Omw+Pjh9cmV0dXJuIGF9fX0sdC50b1JnYlN0cmluZz1mdW5jdGlvbihlLHQpe3ZvaWQgMD09PXQmJih0PTE2KTt2YXIgcj1lWzBdLGk9ZVsxXSxvPWVbMl07cmV0dXJuInJnYjoiK24ocix0KSsiLyIrbihpLHQpKyIvIituKG8sdCl9fSw1NzcwOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuUEFZTE9BRF9MSU1JVD12b2lkIDAsdC5QQVlMT0FEX0xJTUlUPTFlN30sNjM1MTooZSx0LHIpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuRGNzSGFuZGxlcj10LkRjc1BhcnNlcj12b2lkIDA7dmFyIGk9cig0ODIpLG49cig4NzQyKSxvPXIoNTc3MCkscz1bXSxhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX2hhbmRsZXJzPU9iamVjdC5jcmVhdGUobnVsbCksdGhpcy5fYWN0aXZlPXMsdGhpcy5faWRlbnQ9MCx0aGlzLl9oYW5kbGVyRmI9ZnVuY3Rpb24oKXt9LHRoaXMuX3N0YWNrPXtwYXVzZWQ6ITEsbG9vcFBvc2l0aW9uOjAsZmFsbFRocm91Z2g6ITF9fXJldHVybiBlLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5faGFuZGxlcnM9T2JqZWN0LmNyZWF0ZShudWxsKSx0aGlzLl9oYW5kbGVyRmI9ZnVuY3Rpb24oKXt9LHRoaXMuX2FjdGl2ZT1zfSxlLnByb3RvdHlwZS5yZWdpc3RlckhhbmRsZXI9ZnVuY3Rpb24oZSx0KXt2b2lkIDA9PT10aGlzLl9oYW5kbGVyc1tlXSYmKHRoaXMuX2hhbmRsZXJzW2VdPVtdKTt2YXIgcj10aGlzLl9oYW5kbGVyc1tlXTtyZXR1cm4gci5wdXNoKHQpLHtkaXNwb3NlOmZ1bmN0aW9uKCl7dmFyIGU9ci5pbmRleE9mKHQpOy0xIT09ZSYmci5zcGxpY2UoZSwxKX19fSxlLnByb3RvdHlwZS5jbGVhckhhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5faGFuZGxlcnNbZV0mJmRlbGV0ZSB0aGlzLl9oYW5kbGVyc1tlXX0sZS5wcm90b3R5cGUuc2V0SGFuZGxlckZhbGxiYWNrPWZ1bmN0aW9uKGUpe3RoaXMuX2hhbmRsZXJGYj1lfSxlLnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe2lmKHRoaXMuX2FjdGl2ZS5sZW5ndGgpZm9yKHZhciBlPXRoaXMuX3N0YWNrLnBhdXNlZD90aGlzLl9zdGFjay5sb29wUG9zaXRpb24tMTp0aGlzLl9hY3RpdmUubGVuZ3RoLTE7ZT49MDstLWUpdGhpcy5fYWN0aXZlW2VdLnVuaG9vayghMSk7dGhpcy5fc3RhY2sucGF1c2VkPSExLHRoaXMuX2FjdGl2ZT1zLHRoaXMuX2lkZW50PTB9LGUucHJvdG90eXBlLmhvb2s9ZnVuY3Rpb24oZSx0KXtpZih0aGlzLnJlc2V0KCksdGhpcy5faWRlbnQ9ZSx0aGlzLl9hY3RpdmU9dGhpcy5faGFuZGxlcnNbZV18fHMsdGhpcy5fYWN0aXZlLmxlbmd0aClmb3IodmFyIHI9dGhpcy5fYWN0aXZlLmxlbmd0aC0xO3I+PTA7ci0tKXRoaXMuX2FjdGl2ZVtyXS5ob29rKHQpO2Vsc2UgdGhpcy5faGFuZGxlckZiKHRoaXMuX2lkZW50LCJIT09LIix0KX0sZS5wcm90b3R5cGUucHV0PWZ1bmN0aW9uKGUsdCxyKXtpZih0aGlzLl9hY3RpdmUubGVuZ3RoKWZvcih2YXIgbj10aGlzLl9hY3RpdmUubGVuZ3RoLTE7bj49MDtuLS0pdGhpcy5fYWN0aXZlW25dLnB1dChlLHQscik7ZWxzZSB0aGlzLl9oYW5kbGVyRmIodGhpcy5faWRlbnQsIlBVVCIsKDAsaS51dGYzMlRvU3RyaW5nKShlLHQscikpfSxlLnByb3RvdHlwZS51bmhvb2s9ZnVuY3Rpb24oZSx0KXtpZih2b2lkIDA9PT10JiYodD0hMCksdGhpcy5fYWN0aXZlLmxlbmd0aCl7dmFyIHI9ITEsaT10aGlzLl9hY3RpdmUubGVuZ3RoLTEsbj0hMTtpZih0aGlzLl9zdGFjay5wYXVzZWQmJihpPXRoaXMuX3N0YWNrLmxvb3BQb3NpdGlvbi0xLHI9dCxuPXRoaXMuX3N0YWNrLmZhbGxUaHJvdWdoLHRoaXMuX3N0YWNrLnBhdXNlZD0hMSksIW4mJiExPT09cil7Zm9yKDtpPj0wJiYhMCE9PShyPXRoaXMuX2FjdGl2ZVtpXS51bmhvb2soZSkpO2ktLSlpZihyIGluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gdGhpcy5fc3RhY2sucGF1c2VkPSEwLHRoaXMuX3N0YWNrLmxvb3BQb3NpdGlvbj1pLHRoaXMuX3N0YWNrLmZhbGxUaHJvdWdoPSExLHI7aS0tfWZvcig7aT49MDtpLS0paWYoKHI9dGhpcy5fYWN0aXZlW2ldLnVuaG9vayghMSkpaW5zdGFuY2VvZiBQcm9taXNlKXJldHVybiB0aGlzLl9zdGFjay5wYXVzZWQ9ITAsdGhpcy5fc3RhY2subG9vcFBvc2l0aW9uPWksdGhpcy5fc3RhY2suZmFsbFRocm91Z2g9ITAscn1lbHNlIHRoaXMuX2hhbmRsZXJGYih0aGlzLl9pZGVudCwiVU5IT09LIixlKTt0aGlzLl9hY3RpdmU9cyx0aGlzLl9pZGVudD0wfSxlfSgpO3QuRGNzUGFyc2VyPWE7dmFyIGM9bmV3IG4uUGFyYW1zO2MuYWRkUGFyYW0oMCk7dmFyIGw9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe3RoaXMuX2hhbmRsZXI9ZSx0aGlzLl9kYXRhPSIiLHRoaXMuX3BhcmFtcz1jLHRoaXMuX2hpdExpbWl0PSExfXJldHVybiBlLnByb3RvdHlwZS5ob29rPWZ1bmN0aW9uKGUpe3RoaXMuX3BhcmFtcz1lLmxlbmd0aD4xfHxlLnBhcmFtc1swXT9lLmNsb25lKCk6Yyx0aGlzLl9kYXRhPSIiLHRoaXMuX2hpdExpbWl0PSExfSxlLnByb3RvdHlwZS5wdXQ9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2hpdExpbWl0fHwodGhpcy5fZGF0YSs9KDAsaS51dGYzMlRvU3RyaW5nKShlLHQsciksdGhpcy5fZGF0YS5sZW5ndGg+by5QQVlMT0FEX0xJTUlUJiYodGhpcy5fZGF0YT0iIix0aGlzLl9oaXRMaW1pdD0hMCkpfSxlLnByb3RvdHlwZS51bmhvb2s9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxyPSExO2lmKHRoaXMuX2hpdExpbWl0KXI9ITE7ZWxzZSBpZihlJiYocj10aGlzLl9oYW5kbGVyKHRoaXMuX2RhdGEsdGhpcy5fcGFyYW1zKSlpbnN0YW5jZW9mIFByb21pc2UpcmV0dXJuIHIudGhlbigoZnVuY3Rpb24oZSl7cmV0dXJuIHQuX3BhcmFtcz1jLHQuX2RhdGE9IiIsdC5faGl0TGltaXQ9ITEsZX0pKTtyZXR1cm4gdGhpcy5fcGFyYW1zPWMsdGhpcy5fZGF0YT0iIix0aGlzLl9oaXRMaW1pdD0hMSxyfSxlfSgpO3QuRGNzSGFuZGxlcj1sfSwyMDE1OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkVzY2FwZVNlcXVlbmNlUGFyc2VyPXQuVlQ1MDBfVFJBTlNJVElPTl9UQUJMRT10LlRyYW5zaXRpb25UYWJsZT12b2lkIDA7dmFyIG89cig4NDQpLHM9cig4MjczKSxhPXIoODc0MiksYz1yKDYyNDIpLGw9cig2MzUxKSx1PWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLnRhYmxlPW5ldyBVaW50OEFycmF5KGUpfXJldHVybiBlLnByb3RvdHlwZS5zZXREZWZhdWx0PWZ1bmN0aW9uKGUsdCl7KDAscy5maWxsKSh0aGlzLnRhYmxlLGU8PDR8dCl9LGUucHJvdG90eXBlLmFkZD1mdW5jdGlvbihlLHQscixpKXt0aGlzLnRhYmxlW3Q8PDh8ZV09cjw8NHxpfSxlLnByb3RvdHlwZS5hZGRNYW55PWZ1bmN0aW9uKGUsdCxyLGkpe2Zvcih2YXIgbj0wO248ZS5sZW5ndGg7bisrKXRoaXMudGFibGVbdDw8OHxlW25dXT1yPDw0fGl9LGV9KCk7dC5UcmFuc2l0aW9uVGFibGU9dTt2YXIgaD0xNjA7dC5WVDUwMF9UUkFOU0lUSU9OX1RBQkxFPWZ1bmN0aW9uKCl7dmFyIGU9bmV3IHUoNDA5NSksdD1BcnJheS5hcHBseShudWxsLEFycmF5KDI1NikpLm1hcCgoZnVuY3Rpb24oZSx0KXtyZXR1cm4gdH0pKSxyPWZ1bmN0aW9uKGUscil7cmV0dXJuIHQuc2xpY2UoZSxyKX0saT1yKDMyLDEyNyksbj1yKDAsMjQpO24ucHVzaCgyNSksbi5wdXNoLmFwcGx5KG4scigyOCwzMikpO3ZhciBvLHM9cigwLDE0KTtmb3IobyBpbiBlLnNldERlZmF1bHQoMSwwKSxlLmFkZE1hbnkoaSwwLDIsMCkscyllLmFkZE1hbnkoWzI0LDI2LDE1MywxNTRdLG8sMywwKSxlLmFkZE1hbnkocigxMjgsMTQ0KSxvLDMsMCksZS5hZGRNYW55KHIoMTQ0LDE1MiksbywzLDApLGUuYWRkKDE1NixvLDAsMCksZS5hZGQoMjcsbywxMSwxKSxlLmFkZCgxNTcsbyw0LDgpLGUuYWRkTWFueShbMTUyLDE1OCwxNTldLG8sMCw3KSxlLmFkZCgxNTUsbywxMSwzKSxlLmFkZCgxNDQsbywxMSw5KTtyZXR1cm4gZS5hZGRNYW55KG4sMCwzLDApLGUuYWRkTWFueShuLDEsMywxKSxlLmFkZCgxMjcsMSwwLDEpLGUuYWRkTWFueShuLDgsMCw4KSxlLmFkZE1hbnkobiwzLDMsMyksZS5hZGQoMTI3LDMsMCwzKSxlLmFkZE1hbnkobiw0LDMsNCksZS5hZGQoMTI3LDQsMCw0KSxlLmFkZE1hbnkobiw2LDMsNiksZS5hZGRNYW55KG4sNSwzLDUpLGUuYWRkKDEyNyw1LDAsNSksZS5hZGRNYW55KG4sMiwzLDIpLGUuYWRkKDEyNywyLDAsMiksZS5hZGQoOTMsMSw0LDgpLGUuYWRkTWFueShpLDgsNSw4KSxlLmFkZCgxMjcsOCw1LDgpLGUuYWRkTWFueShbMTU2LDI3LDI0LDI2LDddLDgsNiwwKSxlLmFkZE1hbnkocigyOCwzMiksOCwwLDgpLGUuYWRkTWFueShbODgsOTQsOTVdLDEsMCw3KSxlLmFkZE1hbnkoaSw3LDAsNyksZS5hZGRNYW55KG4sNywwLDcpLGUuYWRkKDE1Niw3LDAsMCksZS5hZGQoMTI3LDcsMCw3KSxlLmFkZCg5MSwxLDExLDMpLGUuYWRkTWFueShyKDY0LDEyNyksMyw3LDApLGUuYWRkTWFueShyKDQ4LDYwKSwzLDgsNCksZS5hZGRNYW55KFs2MCw2MSw2Miw2M10sMyw5LDQpLGUuYWRkTWFueShyKDQ4LDYwKSw0LDgsNCksZS5hZGRNYW55KHIoNjQsMTI3KSw0LDcsMCksZS5hZGRNYW55KFs2MCw2MSw2Miw2M10sNCwwLDYpLGUuYWRkTWFueShyKDMyLDY0KSw2LDAsNiksZS5hZGQoMTI3LDYsMCw2KSxlLmFkZE1hbnkocig2NCwxMjcpLDYsMCwwKSxlLmFkZE1hbnkocigzMiw0OCksMyw5LDUpLGUuYWRkTWFueShyKDMyLDQ4KSw1LDksNSksZS5hZGRNYW55KHIoNDgsNjQpLDUsMCw2KSxlLmFkZE1hbnkocig2NCwxMjcpLDUsNywwKSxlLmFkZE1hbnkocigzMiw0OCksNCw5LDUpLGUuYWRkTWFueShyKDMyLDQ4KSwxLDksMiksZS5hZGRNYW55KHIoMzIsNDgpLDIsOSwyKSxlLmFkZE1hbnkocig0OCwxMjcpLDIsMTAsMCksZS5hZGRNYW55KHIoNDgsODApLDEsMTAsMCksZS5hZGRNYW55KHIoODEsODgpLDEsMTAsMCksZS5hZGRNYW55KFs4OSw5MCw5Ml0sMSwxMCwwKSxlLmFkZE1hbnkocig5NiwxMjcpLDEsMTAsMCksZS5hZGQoODAsMSwxMSw5KSxlLmFkZE1hbnkobiw5LDAsOSksZS5hZGQoMTI3LDksMCw5KSxlLmFkZE1hbnkocigyOCwzMiksOSwwLDkpLGUuYWRkTWFueShyKDMyLDQ4KSw5LDksMTIpLGUuYWRkTWFueShyKDQ4LDYwKSw5LDgsMTApLGUuYWRkTWFueShbNjAsNjEsNjIsNjNdLDksOSwxMCksZS5hZGRNYW55KG4sMTEsMCwxMSksZS5hZGRNYW55KHIoMzIsMTI4KSwxMSwwLDExKSxlLmFkZE1hbnkocigyOCwzMiksMTEsMCwxMSksZS5hZGRNYW55KG4sMTAsMCwxMCksZS5hZGQoMTI3LDEwLDAsMTApLGUuYWRkTWFueShyKDI4LDMyKSwxMCwwLDEwKSxlLmFkZE1hbnkocig0OCw2MCksMTAsOCwxMCksZS5hZGRNYW55KFs2MCw2MSw2Miw2M10sMTAsMCwxMSksZS5hZGRNYW55KHIoMzIsNDgpLDEwLDksMTIpLGUuYWRkTWFueShuLDEyLDAsMTIpLGUuYWRkKDEyNywxMiwwLDEyKSxlLmFkZE1hbnkocigyOCwzMiksMTIsMCwxMiksZS5hZGRNYW55KHIoMzIsNDgpLDEyLDksMTIpLGUuYWRkTWFueShyKDQ4LDY0KSwxMiwwLDExKSxlLmFkZE1hbnkocig2NCwxMjcpLDEyLDEyLDEzKSxlLmFkZE1hbnkocig2NCwxMjcpLDEwLDEyLDEzKSxlLmFkZE1hbnkocig2NCwxMjcpLDksMTIsMTMpLGUuYWRkTWFueShuLDEzLDEzLDEzKSxlLmFkZE1hbnkoaSwxMywxMywxMyksZS5hZGQoMTI3LDEzLDAsMTMpLGUuYWRkTWFueShbMjcsMTU2LDI0LDI2XSwxMywxNCwwKSxlLmFkZChoLDAsMiwwKSxlLmFkZChoLDgsNSw4KSxlLmFkZChoLDYsMCw2KSxlLmFkZChoLDExLDAsMTEpLGUuYWRkKGgsMTMsMTMsMTMpLGV9KCk7dmFyIGY9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gcihyKXt2b2lkIDA9PT1yJiYocj10LlZUNTAwX1RSQU5TSVRJT05fVEFCTEUpO3ZhciBpPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gaS5fdHJhbnNpdGlvbnM9cixpLl9wYXJzZVN0YWNrPXtzdGF0ZTowLGhhbmRsZXJzOltdLGhhbmRsZXJQb3M6MCx0cmFuc2l0aW9uOjAsY2h1bmtQb3M6MH0saS5pbml0aWFsU3RhdGU9MCxpLmN1cnJlbnRTdGF0ZT1pLmluaXRpYWxTdGF0ZSxpLl9wYXJhbXM9bmV3IGEuUGFyYW1zLGkuX3BhcmFtcy5hZGRQYXJhbSgwKSxpLl9jb2xsZWN0PTAsaS5wcmVjZWRpbmdDb2RlcG9pbnQ9MCxpLl9wcmludEhhbmRsZXJGYj1mdW5jdGlvbihlLHQscil7fSxpLl9leGVjdXRlSGFuZGxlckZiPWZ1bmN0aW9uKGUpe30saS5fY3NpSGFuZGxlckZiPWZ1bmN0aW9uKGUsdCl7fSxpLl9lc2NIYW5kbGVyRmI9ZnVuY3Rpb24oZSl7fSxpLl9lcnJvckhhbmRsZXJGYj1mdW5jdGlvbihlKXtyZXR1cm4gZX0saS5fcHJpbnRIYW5kbGVyPWkuX3ByaW50SGFuZGxlckZiLGkuX2V4ZWN1dGVIYW5kbGVycz1PYmplY3QuY3JlYXRlKG51bGwpLGkuX2NzaUhhbmRsZXJzPU9iamVjdC5jcmVhdGUobnVsbCksaS5fZXNjSGFuZGxlcnM9T2JqZWN0LmNyZWF0ZShudWxsKSxpLl9vc2NQYXJzZXI9bmV3IGMuT3NjUGFyc2VyLGkuX2Rjc1BhcnNlcj1uZXcgbC5EY3NQYXJzZXIsaS5fZXJyb3JIYW5kbGVyPWkuX2Vycm9ySGFuZGxlckZiLGkucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiXFwifSwoZnVuY3Rpb24oKXtyZXR1cm4hMH0pKSxpfXJldHVybiBuKHIsZSksci5wcm90b3R5cGUuX2lkZW50aWZpZXI9ZnVuY3Rpb24oZSx0KXt2b2lkIDA9PT10JiYodD1bNjQsMTI2XSk7dmFyIHI9MDtpZihlLnByZWZpeCl7aWYoZS5wcmVmaXgubGVuZ3RoPjEpdGhyb3cgbmV3IEVycm9yKCJvbmx5IG9uZSBieXRlIGFzIHByZWZpeCBzdXBwb3J0ZWQiKTtpZigocj1lLnByZWZpeC5jaGFyQ29kZUF0KDApKSYmNjA+cnx8cj42Myl0aHJvdyBuZXcgRXJyb3IoInByZWZpeCBtdXN0IGJlIGluIHJhbmdlIDB4M2MgLi4gMHgzZiIpfWlmKGUuaW50ZXJtZWRpYXRlcyl7aWYoZS5pbnRlcm1lZGlhdGVzLmxlbmd0aD4yKXRocm93IG5ldyBFcnJvcigib25seSB0d28gYnl0ZXMgYXMgaW50ZXJtZWRpYXRlcyBhcmUgc3VwcG9ydGVkIik7Zm9yKHZhciBpPTA7aTxlLmludGVybWVkaWF0ZXMubGVuZ3RoOysraSl7dmFyIG49ZS5pbnRlcm1lZGlhdGVzLmNoYXJDb2RlQXQoaSk7aWYoMzI+bnx8bj40Nyl0aHJvdyBuZXcgRXJyb3IoImludGVybWVkaWF0ZSBtdXN0IGJlIGluIHJhbmdlIDB4MjAgLi4gMHgyZiIpO3I8PD04LHJ8PW59fWlmKDEhPT1lLmZpbmFsLmxlbmd0aCl0aHJvdyBuZXcgRXJyb3IoImZpbmFsIG11c3QgYmUgYSBzaW5nbGUgYnl0ZSIpO3ZhciBvPWUuZmluYWwuY2hhckNvZGVBdCgwKTtpZih0WzBdPm98fG8+dFsxXSl0aHJvdyBuZXcgRXJyb3IoImZpbmFsIG11c3QgYmUgaW4gcmFuZ2UgIit0WzBdKyIgLi4gIit0WzFdKTtyZXR1cm4ocjw8PTgpfG99LHIucHJvdG90eXBlLmlkZW50VG9TdHJpbmc9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PVtdO2U7KXQucHVzaChTdHJpbmcuZnJvbUNoYXJDb2RlKDI1NSZlKSksZT4+PTg7cmV0dXJuIHQucmV2ZXJzZSgpLmpvaW4oIiIpfSxyLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5fY3NpSGFuZGxlcnM9T2JqZWN0LmNyZWF0ZShudWxsKSx0aGlzLl9leGVjdXRlSGFuZGxlcnM9T2JqZWN0LmNyZWF0ZShudWxsKSx0aGlzLl9lc2NIYW5kbGVycz1PYmplY3QuY3JlYXRlKG51bGwpLHRoaXMuX29zY1BhcnNlci5kaXNwb3NlKCksdGhpcy5fZGNzUGFyc2VyLmRpc3Bvc2UoKX0sci5wcm90b3R5cGUuc2V0UHJpbnRIYW5kbGVyPWZ1bmN0aW9uKGUpe3RoaXMuX3ByaW50SGFuZGxlcj1lfSxyLnByb3RvdHlwZS5jbGVhclByaW50SGFuZGxlcj1mdW5jdGlvbigpe3RoaXMuX3ByaW50SGFuZGxlcj10aGlzLl9wcmludEhhbmRsZXJGYn0sci5wcm90b3R5cGUucmVnaXN0ZXJFc2NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5faWRlbnRpZmllcihlLFs0OCwxMjZdKTt2b2lkIDA9PT10aGlzLl9lc2NIYW5kbGVyc1tyXSYmKHRoaXMuX2VzY0hhbmRsZXJzW3JdPVtdKTt2YXIgaT10aGlzLl9lc2NIYW5kbGVyc1tyXTtyZXR1cm4gaS5wdXNoKHQpLHtkaXNwb3NlOmZ1bmN0aW9uKCl7dmFyIGU9aS5pbmRleE9mKHQpOy0xIT09ZSYmaS5zcGxpY2UoZSwxKX19fSxyLnByb3RvdHlwZS5jbGVhckVzY0hhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5fZXNjSGFuZGxlcnNbdGhpcy5faWRlbnRpZmllcihlLFs0OCwxMjZdKV0mJmRlbGV0ZSB0aGlzLl9lc2NIYW5kbGVyc1t0aGlzLl9pZGVudGlmaWVyKGUsWzQ4LDEyNl0pXX0sci5wcm90b3R5cGUuc2V0RXNjSGFuZGxlckZhbGxiYWNrPWZ1bmN0aW9uKGUpe3RoaXMuX2VzY0hhbmRsZXJGYj1lfSxyLnByb3RvdHlwZS5zZXRFeGVjdXRlSGFuZGxlcj1mdW5jdGlvbihlLHQpe3RoaXMuX2V4ZWN1dGVIYW5kbGVyc1tlLmNoYXJDb2RlQXQoMCldPXR9LHIucHJvdG90eXBlLmNsZWFyRXhlY3V0ZUhhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5fZXhlY3V0ZUhhbmRsZXJzW2UuY2hhckNvZGVBdCgwKV0mJmRlbGV0ZSB0aGlzLl9leGVjdXRlSGFuZGxlcnNbZS5jaGFyQ29kZUF0KDApXX0sci5wcm90b3R5cGUuc2V0RXhlY3V0ZUhhbmRsZXJGYWxsYmFjaz1mdW5jdGlvbihlKXt0aGlzLl9leGVjdXRlSGFuZGxlckZiPWV9LHIucHJvdG90eXBlLnJlZ2lzdGVyQ3NpSGFuZGxlcj1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX2lkZW50aWZpZXIoZSk7dm9pZCAwPT09dGhpcy5fY3NpSGFuZGxlcnNbcl0mJih0aGlzLl9jc2lIYW5kbGVyc1tyXT1bXSk7dmFyIGk9dGhpcy5fY3NpSGFuZGxlcnNbcl07cmV0dXJuIGkucHVzaCh0KSx7ZGlzcG9zZTpmdW5jdGlvbigpe3ZhciBlPWkuaW5kZXhPZih0KTstMSE9PWUmJmkuc3BsaWNlKGUsMSl9fX0sci5wcm90b3R5cGUuY2xlYXJDc2lIYW5kbGVyPWZ1bmN0aW9uKGUpe3RoaXMuX2NzaUhhbmRsZXJzW3RoaXMuX2lkZW50aWZpZXIoZSldJiZkZWxldGUgdGhpcy5fY3NpSGFuZGxlcnNbdGhpcy5faWRlbnRpZmllcihlKV19LHIucHJvdG90eXBlLnNldENzaUhhbmRsZXJGYWxsYmFjaz1mdW5jdGlvbihlKXt0aGlzLl9jc2lIYW5kbGVyRmI9ZX0sci5wcm90b3R5cGUucmVnaXN0ZXJEY3NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMuX2Rjc1BhcnNlci5yZWdpc3RlckhhbmRsZXIodGhpcy5faWRlbnRpZmllcihlKSx0KX0sci5wcm90b3R5cGUuY2xlYXJEY3NIYW5kbGVyPWZ1bmN0aW9uKGUpe3RoaXMuX2Rjc1BhcnNlci5jbGVhckhhbmRsZXIodGhpcy5faWRlbnRpZmllcihlKSl9LHIucHJvdG90eXBlLnNldERjc0hhbmRsZXJGYWxsYmFjaz1mdW5jdGlvbihlKXt0aGlzLl9kY3NQYXJzZXIuc2V0SGFuZGxlckZhbGxiYWNrKGUpfSxyLnByb3RvdHlwZS5yZWdpc3Rlck9zY0hhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5fb3NjUGFyc2VyLnJlZ2lzdGVySGFuZGxlcihlLHQpfSxyLnByb3RvdHlwZS5jbGVhck9zY0hhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5fb3NjUGFyc2VyLmNsZWFySGFuZGxlcihlKX0sci5wcm90b3R5cGUuc2V0T3NjSGFuZGxlckZhbGxiYWNrPWZ1bmN0aW9uKGUpe3RoaXMuX29zY1BhcnNlci5zZXRIYW5kbGVyRmFsbGJhY2soZSl9LHIucHJvdG90eXBlLnNldEVycm9ySGFuZGxlcj1mdW5jdGlvbihlKXt0aGlzLl9lcnJvckhhbmRsZXI9ZX0sci5wcm90b3R5cGUuY2xlYXJFcnJvckhhbmRsZXI9ZnVuY3Rpb24oKXt0aGlzLl9lcnJvckhhbmRsZXI9dGhpcy5fZXJyb3JIYW5kbGVyRmJ9LHIucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5jdXJyZW50U3RhdGU9dGhpcy5pbml0aWFsU3RhdGUsdGhpcy5fb3NjUGFyc2VyLnJlc2V0KCksdGhpcy5fZGNzUGFyc2VyLnJlc2V0KCksdGhpcy5fcGFyYW1zLnJlc2V0KCksdGhpcy5fcGFyYW1zLmFkZFBhcmFtKDApLHRoaXMuX2NvbGxlY3Q9MCx0aGlzLnByZWNlZGluZ0NvZGVwb2ludD0wLDAhPT10aGlzLl9wYXJzZVN0YWNrLnN0YXRlJiYodGhpcy5fcGFyc2VTdGFjay5zdGF0ZT0yLHRoaXMuX3BhcnNlU3RhY2suaGFuZGxlcnM9W10pfSxyLnByb3RvdHlwZS5fcHJlc2VydmVTdGFjaz1mdW5jdGlvbihlLHQscixpLG4pe3RoaXMuX3BhcnNlU3RhY2suc3RhdGU9ZSx0aGlzLl9wYXJzZVN0YWNrLmhhbmRsZXJzPXQsdGhpcy5fcGFyc2VTdGFjay5oYW5kbGVyUG9zPXIsdGhpcy5fcGFyc2VTdGFjay50cmFuc2l0aW9uPWksdGhpcy5fcGFyc2VTdGFjay5jaHVua1Bvcz1ufSxyLnByb3RvdHlwZS5wYXJzZT1mdW5jdGlvbihlLHQscil7dmFyIGksbj0wLG89MCxzPTA7aWYodGhpcy5fcGFyc2VTdGFjay5zdGF0ZSlpZigyPT09dGhpcy5fcGFyc2VTdGFjay5zdGF0ZSl0aGlzLl9wYXJzZVN0YWNrLnN0YXRlPTAscz10aGlzLl9wYXJzZVN0YWNrLmNodW5rUG9zKzE7ZWxzZXtpZih2b2lkIDA9PT1yfHwxPT09dGhpcy5fcGFyc2VTdGFjay5zdGF0ZSl0aHJvdyB0aGlzLl9wYXJzZVN0YWNrLnN0YXRlPTEsbmV3IEVycm9yKCJpbXByb3BlciBjb250aW51YXRpb24gZHVlIHRvIHByZXZpb3VzIGFzeW5jIGhhbmRsZXIsIGdpdmluZyB1cCBwYXJzaW5nIik7dmFyIGE9dGhpcy5fcGFyc2VTdGFjay5oYW5kbGVycyxjPXRoaXMuX3BhcnNlU3RhY2suaGFuZGxlclBvcy0xO3N3aXRjaCh0aGlzLl9wYXJzZVN0YWNrLnN0YXRlKXtjYXNlIDM6aWYoITE9PT1yJiZjPi0xKWZvcig7Yz49MCYmITAhPT0oaT1hW2NdKHRoaXMuX3BhcmFtcykpO2MtLSlpZihpIGluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gdGhpcy5fcGFyc2VTdGFjay5oYW5kbGVyUG9zPWMsaTt0aGlzLl9wYXJzZVN0YWNrLmhhbmRsZXJzPVtdO2JyZWFrO2Nhc2UgNDppZighMT09PXImJmM+LTEpZm9yKDtjPj0wJiYhMCE9PShpPWFbY10oKSk7Yy0tKWlmKGkgaW5zdGFuY2VvZiBQcm9taXNlKXJldHVybiB0aGlzLl9wYXJzZVN0YWNrLmhhbmRsZXJQb3M9YyxpO3RoaXMuX3BhcnNlU3RhY2suaGFuZGxlcnM9W107YnJlYWs7Y2FzZSA2OmlmKG49ZVt0aGlzLl9wYXJzZVN0YWNrLmNodW5rUG9zXSxpPXRoaXMuX2Rjc1BhcnNlci51bmhvb2soMjQhPT1uJiYyNiE9PW4scikpcmV0dXJuIGk7Mjc9PT1uJiYodGhpcy5fcGFyc2VTdGFjay50cmFuc2l0aW9ufD0xKSx0aGlzLl9wYXJhbXMucmVzZXQoKSx0aGlzLl9wYXJhbXMuYWRkUGFyYW0oMCksdGhpcy5fY29sbGVjdD0wO2JyZWFrO2Nhc2UgNTppZihuPWVbdGhpcy5fcGFyc2VTdGFjay5jaHVua1Bvc10saT10aGlzLl9vc2NQYXJzZXIuZW5kKDI0IT09biYmMjYhPT1uLHIpKXJldHVybiBpOzI3PT09biYmKHRoaXMuX3BhcnNlU3RhY2sudHJhbnNpdGlvbnw9MSksdGhpcy5fcGFyYW1zLnJlc2V0KCksdGhpcy5fcGFyYW1zLmFkZFBhcmFtKDApLHRoaXMuX2NvbGxlY3Q9MH10aGlzLl9wYXJzZVN0YWNrLnN0YXRlPTAscz10aGlzLl9wYXJzZVN0YWNrLmNodW5rUG9zKzEsdGhpcy5wcmVjZWRpbmdDb2RlcG9pbnQ9MCx0aGlzLmN1cnJlbnRTdGF0ZT0xNSZ0aGlzLl9wYXJzZVN0YWNrLnRyYW5zaXRpb259Zm9yKHZhciBsPXM7bDx0OysrbCl7c3dpdGNoKG49ZVtsXSwobz10aGlzLl90cmFuc2l0aW9ucy50YWJsZVt0aGlzLmN1cnJlbnRTdGF0ZTw8OHwobjwxNjA/bjpoKV0pPj40KXtjYXNlIDI6Zm9yKHZhciB1PWwrMTs7Kyt1KXtpZih1Pj10fHwobj1lW3VdKTwzMnx8bj4xMjYmJm48aCl7dGhpcy5fcHJpbnRIYW5kbGVyKGUsbCx1KSxsPXUtMTticmVha31pZigrK3U+PXR8fChuPWVbdV0pPDMyfHxuPjEyNiYmbjxoKXt0aGlzLl9wcmludEhhbmRsZXIoZSxsLHUpLGw9dS0xO2JyZWFrfWlmKCsrdT49dHx8KG49ZVt1XSk8MzJ8fG4+MTI2JiZuPGgpe3RoaXMuX3ByaW50SGFuZGxlcihlLGwsdSksbD11LTE7YnJlYWt9aWYoKyt1Pj10fHwobj1lW3VdKTwzMnx8bj4xMjYmJm48aCl7dGhpcy5fcHJpbnRIYW5kbGVyKGUsbCx1KSxsPXUtMTticmVha319YnJlYWs7Y2FzZSAzOnRoaXMuX2V4ZWN1dGVIYW5kbGVyc1tuXT90aGlzLl9leGVjdXRlSGFuZGxlcnNbbl0oKTp0aGlzLl9leGVjdXRlSGFuZGxlckZiKG4pLHRoaXMucHJlY2VkaW5nQ29kZXBvaW50PTA7YnJlYWs7Y2FzZSAwOmJyZWFrO2Nhc2UgMTppZih0aGlzLl9lcnJvckhhbmRsZXIoe3Bvc2l0aW9uOmwsY29kZTpuLGN1cnJlbnRTdGF0ZTp0aGlzLmN1cnJlbnRTdGF0ZSxjb2xsZWN0OnRoaXMuX2NvbGxlY3QscGFyYW1zOnRoaXMuX3BhcmFtcyxhYm9ydDohMX0pLmFib3J0KXJldHVybjticmVhaztjYXNlIDc6Zm9yKHZhciBmPShhPXRoaXMuX2NzaUhhbmRsZXJzW3RoaXMuX2NvbGxlY3Q8PDh8bl0pP2EubGVuZ3RoLTE6LTE7Zj49MCYmITAhPT0oaT1hW2ZdKHRoaXMuX3BhcmFtcykpO2YtLSlpZihpIGluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gdGhpcy5fcHJlc2VydmVTdGFjaygzLGEsZixvLGwpLGk7ZjwwJiZ0aGlzLl9jc2lIYW5kbGVyRmIodGhpcy5fY29sbGVjdDw8OHxuLHRoaXMuX3BhcmFtcyksdGhpcy5wcmVjZWRpbmdDb2RlcG9pbnQ9MDticmVhaztjYXNlIDg6ZG97c3dpdGNoKG4pe2Nhc2UgNTk6dGhpcy5fcGFyYW1zLmFkZFBhcmFtKDApO2JyZWFrO2Nhc2UgNTg6dGhpcy5fcGFyYW1zLmFkZFN1YlBhcmFtKC0xKTticmVhaztkZWZhdWx0OnRoaXMuX3BhcmFtcy5hZGREaWdpdChuLTQ4KX19d2hpbGUoKytsPHQmJihuPWVbbF0pPjQ3JiZuPDYwKTtsLS07YnJlYWs7Y2FzZSA5OnRoaXMuX2NvbGxlY3Q8PD04LHRoaXMuX2NvbGxlY3R8PW47YnJlYWs7Y2FzZSAxMDpmb3IodmFyIF89dGhpcy5fZXNjSGFuZGxlcnNbdGhpcy5fY29sbGVjdDw8OHxuXSxkPV8/Xy5sZW5ndGgtMTotMTtkPj0wJiYhMCE9PShpPV9bZF0oKSk7ZC0tKWlmKGkgaW5zdGFuY2VvZiBQcm9taXNlKXJldHVybiB0aGlzLl9wcmVzZXJ2ZVN0YWNrKDQsXyxkLG8sbCksaTtkPDAmJnRoaXMuX2VzY0hhbmRsZXJGYih0aGlzLl9jb2xsZWN0PDw4fG4pLHRoaXMucHJlY2VkaW5nQ29kZXBvaW50PTA7YnJlYWs7Y2FzZSAxMTp0aGlzLl9wYXJhbXMucmVzZXQoKSx0aGlzLl9wYXJhbXMuYWRkUGFyYW0oMCksdGhpcy5fY29sbGVjdD0wO2JyZWFrO2Nhc2UgMTI6dGhpcy5fZGNzUGFyc2VyLmhvb2sodGhpcy5fY29sbGVjdDw8OHxuLHRoaXMuX3BhcmFtcyk7YnJlYWs7Y2FzZSAxMzpmb3IodmFyIHA9bCsxOzsrK3ApaWYocD49dHx8MjQ9PT0obj1lW3BdKXx8MjY9PT1ufHwyNz09PW58fG4+MTI3JiZuPGgpe3RoaXMuX2Rjc1BhcnNlci5wdXQoZSxsLHApLGw9cC0xO2JyZWFrfWJyZWFrO2Nhc2UgMTQ6aWYoaT10aGlzLl9kY3NQYXJzZXIudW5ob29rKDI0IT09biYmMjYhPT1uKSlyZXR1cm4gdGhpcy5fcHJlc2VydmVTdGFjayg2LFtdLDAsbyxsKSxpOzI3PT09biYmKG98PTEpLHRoaXMuX3BhcmFtcy5yZXNldCgpLHRoaXMuX3BhcmFtcy5hZGRQYXJhbSgwKSx0aGlzLl9jb2xsZWN0PTAsdGhpcy5wcmVjZWRpbmdDb2RlcG9pbnQ9MDticmVhaztjYXNlIDQ6dGhpcy5fb3NjUGFyc2VyLnN0YXJ0KCk7YnJlYWs7Y2FzZSA1OmZvcih2YXIgdj1sKzE7O3YrKylpZih2Pj10fHwobj1lW3ZdKTwzMnx8bj4xMjcmJm48aCl7dGhpcy5fb3NjUGFyc2VyLnB1dChlLGwsdiksbD12LTE7YnJlYWt9YnJlYWs7Y2FzZSA2OmlmKGk9dGhpcy5fb3NjUGFyc2VyLmVuZCgyNCE9PW4mJjI2IT09bikpcmV0dXJuIHRoaXMuX3ByZXNlcnZlU3RhY2soNSxbXSwwLG8sbCksaTsyNz09PW4mJihvfD0xKSx0aGlzLl9wYXJhbXMucmVzZXQoKSx0aGlzLl9wYXJhbXMuYWRkUGFyYW0oMCksdGhpcy5fY29sbGVjdD0wLHRoaXMucHJlY2VkaW5nQ29kZXBvaW50PTB9dGhpcy5jdXJyZW50U3RhdGU9MTUmb319LHJ9KG8uRGlzcG9zYWJsZSk7dC5Fc2NhcGVTZXF1ZW5jZVBhcnNlcj1mfSw2MjQyOihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Pc2NIYW5kbGVyPXQuT3NjUGFyc2VyPXZvaWQgMDt2YXIgaT1yKDU3NzApLG49cig0ODIpLG89W10scz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt0aGlzLl9zdGF0ZT0wLHRoaXMuX2FjdGl2ZT1vLHRoaXMuX2lkPS0xLHRoaXMuX2hhbmRsZXJzPU9iamVjdC5jcmVhdGUobnVsbCksdGhpcy5faGFuZGxlckZiPWZ1bmN0aW9uKCl7fSx0aGlzLl9zdGFjaz17cGF1c2VkOiExLGxvb3BQb3NpdGlvbjowLGZhbGxUaHJvdWdoOiExfX1yZXR1cm4gZS5wcm90b3R5cGUucmVnaXN0ZXJIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7dm9pZCAwPT09dGhpcy5faGFuZGxlcnNbZV0mJih0aGlzLl9oYW5kbGVyc1tlXT1bXSk7dmFyIHI9dGhpcy5faGFuZGxlcnNbZV07cmV0dXJuIHIucHVzaCh0KSx7ZGlzcG9zZTpmdW5jdGlvbigpe3ZhciBlPXIuaW5kZXhPZih0KTstMSE9PWUmJnIuc3BsaWNlKGUsMSl9fX0sZS5wcm90b3R5cGUuY2xlYXJIYW5kbGVyPWZ1bmN0aW9uKGUpe3RoaXMuX2hhbmRsZXJzW2VdJiZkZWxldGUgdGhpcy5faGFuZGxlcnNbZV19LGUucHJvdG90eXBlLnNldEhhbmRsZXJGYWxsYmFjaz1mdW5jdGlvbihlKXt0aGlzLl9oYW5kbGVyRmI9ZX0sZS5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3RoaXMuX2hhbmRsZXJzPU9iamVjdC5jcmVhdGUobnVsbCksdGhpcy5faGFuZGxlckZiPWZ1bmN0aW9uKCl7fSx0aGlzLl9hY3RpdmU9b30sZS5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXtpZigyPT09dGhpcy5fc3RhdGUpZm9yKHZhciBlPXRoaXMuX3N0YWNrLnBhdXNlZD90aGlzLl9zdGFjay5sb29wUG9zaXRpb24tMTp0aGlzLl9hY3RpdmUubGVuZ3RoLTE7ZT49MDstLWUpdGhpcy5fYWN0aXZlW2VdLmVuZCghMSk7dGhpcy5fc3RhY2sucGF1c2VkPSExLHRoaXMuX2FjdGl2ZT1vLHRoaXMuX2lkPS0xLHRoaXMuX3N0YXRlPTB9LGUucHJvdG90eXBlLl9zdGFydD1mdW5jdGlvbigpe2lmKHRoaXMuX2FjdGl2ZT10aGlzLl9oYW5kbGVyc1t0aGlzLl9pZF18fG8sdGhpcy5fYWN0aXZlLmxlbmd0aClmb3IodmFyIGU9dGhpcy5fYWN0aXZlLmxlbmd0aC0xO2U+PTA7ZS0tKXRoaXMuX2FjdGl2ZVtlXS5zdGFydCgpO2Vsc2UgdGhpcy5faGFuZGxlckZiKHRoaXMuX2lkLCJTVEFSVCIpfSxlLnByb3RvdHlwZS5fcHV0PWZ1bmN0aW9uKGUsdCxyKXtpZih0aGlzLl9hY3RpdmUubGVuZ3RoKWZvcih2YXIgaT10aGlzLl9hY3RpdmUubGVuZ3RoLTE7aT49MDtpLS0pdGhpcy5fYWN0aXZlW2ldLnB1dChlLHQscik7ZWxzZSB0aGlzLl9oYW5kbGVyRmIodGhpcy5faWQsIlBVVCIsKDAsbi51dGYzMlRvU3RyaW5nKShlLHQscikpfSxlLnByb3RvdHlwZS5zdGFydD1mdW5jdGlvbigpe3RoaXMucmVzZXQoKSx0aGlzLl9zdGF0ZT0xfSxlLnByb3RvdHlwZS5wdXQ9ZnVuY3Rpb24oZSx0LHIpe2lmKDMhPT10aGlzLl9zdGF0ZSl7aWYoMT09PXRoaXMuX3N0YXRlKWZvcig7dDxyOyl7dmFyIGk9ZVt0KytdO2lmKDU5PT09aSl7dGhpcy5fc3RhdGU9Mix0aGlzLl9zdGFydCgpO2JyZWFrfWlmKGk8NDh8fDU3PGkpcmV0dXJuIHZvaWQodGhpcy5fc3RhdGU9Myk7LTE9PT10aGlzLl9pZCYmKHRoaXMuX2lkPTApLHRoaXMuX2lkPTEwKnRoaXMuX2lkK2ktNDh9Mj09PXRoaXMuX3N0YXRlJiZyLXQ+MCYmdGhpcy5fcHV0KGUsdCxyKX19LGUucHJvdG90eXBlLmVuZD1mdW5jdGlvbihlLHQpe2lmKHZvaWQgMD09PXQmJih0PSEwKSwwIT09dGhpcy5fc3RhdGUpe2lmKDMhPT10aGlzLl9zdGF0ZSlpZigxPT09dGhpcy5fc3RhdGUmJnRoaXMuX3N0YXJ0KCksdGhpcy5fYWN0aXZlLmxlbmd0aCl7dmFyIHI9ITEsaT10aGlzLl9hY3RpdmUubGVuZ3RoLTEsbj0hMTtpZih0aGlzLl9zdGFjay5wYXVzZWQmJihpPXRoaXMuX3N0YWNrLmxvb3BQb3NpdGlvbi0xLHI9dCxuPXRoaXMuX3N0YWNrLmZhbGxUaHJvdWdoLHRoaXMuX3N0YWNrLnBhdXNlZD0hMSksIW4mJiExPT09cil7Zm9yKDtpPj0wJiYhMCE9PShyPXRoaXMuX2FjdGl2ZVtpXS5lbmQoZSkpO2ktLSlpZihyIGluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gdGhpcy5fc3RhY2sucGF1c2VkPSEwLHRoaXMuX3N0YWNrLmxvb3BQb3NpdGlvbj1pLHRoaXMuX3N0YWNrLmZhbGxUaHJvdWdoPSExLHI7aS0tfWZvcig7aT49MDtpLS0paWYoKHI9dGhpcy5fYWN0aXZlW2ldLmVuZCghMSkpaW5zdGFuY2VvZiBQcm9taXNlKXJldHVybiB0aGlzLl9zdGFjay5wYXVzZWQ9ITAsdGhpcy5fc3RhY2subG9vcFBvc2l0aW9uPWksdGhpcy5fc3RhY2suZmFsbFRocm91Z2g9ITAscn1lbHNlIHRoaXMuX2hhbmRsZXJGYih0aGlzLl9pZCwiRU5EIixlKTt0aGlzLl9hY3RpdmU9byx0aGlzLl9pZD0tMSx0aGlzLl9zdGF0ZT0wfX0sZX0oKTt0Lk9zY1BhcnNlcj1zO3ZhciBhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9oYW5kbGVyPWUsdGhpcy5fZGF0YT0iIix0aGlzLl9oaXRMaW1pdD0hMX1yZXR1cm4gZS5wcm90b3R5cGUuc3RhcnQ9ZnVuY3Rpb24oKXt0aGlzLl9kYXRhPSIiLHRoaXMuX2hpdExpbWl0PSExfSxlLnByb3RvdHlwZS5wdXQ9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2hpdExpbWl0fHwodGhpcy5fZGF0YSs9KDAsbi51dGYzMlRvU3RyaW5nKShlLHQsciksdGhpcy5fZGF0YS5sZW5ndGg+aS5QQVlMT0FEX0xJTUlUJiYodGhpcy5fZGF0YT0iIix0aGlzLl9oaXRMaW1pdD0hMCkpfSxlLnByb3RvdHlwZS5lbmQ9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxyPSExO2lmKHRoaXMuX2hpdExpbWl0KXI9ITE7ZWxzZSBpZihlJiYocj10aGlzLl9oYW5kbGVyKHRoaXMuX2RhdGEpKWluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gci50aGVuKChmdW5jdGlvbihlKXtyZXR1cm4gdC5fZGF0YT0iIix0Ll9oaXRMaW1pdD0hMSxlfSkpO3JldHVybiB0aGlzLl9kYXRhPSIiLHRoaXMuX2hpdExpbWl0PSExLHJ9LGV9KCk7dC5Pc2NIYW5kbGVyPWF9LDg3NDI6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5QYXJhbXM9dm9pZCAwO3ZhciByPTIxNDc0ODM2NDcsaT1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSx0KXtpZih2b2lkIDA9PT1lJiYoZT0zMiksdm9pZCAwPT09dCYmKHQ9MzIpLHRoaXMubWF4TGVuZ3RoPWUsdGhpcy5tYXhTdWJQYXJhbXNMZW5ndGg9dCx0PjI1Nil0aHJvdyBuZXcgRXJyb3IoIm1heFN1YlBhcmFtc0xlbmd0aCBtdXN0IG5vdCBiZSBncmVhdGVyIHRoYW4gMjU2Iik7dGhpcy5wYXJhbXM9bmV3IEludDMyQXJyYXkoZSksdGhpcy5sZW5ndGg9MCx0aGlzLl9zdWJQYXJhbXM9bmV3IEludDMyQXJyYXkodCksdGhpcy5fc3ViUGFyYW1zTGVuZ3RoPTAsdGhpcy5fc3ViUGFyYW1zSWR4PW5ldyBVaW50MTZBcnJheShlKSx0aGlzLl9yZWplY3REaWdpdHM9ITEsdGhpcy5fcmVqZWN0U3ViRGlnaXRzPSExLHRoaXMuX2RpZ2l0SXNTdWI9ITF9cmV0dXJuIGUuZnJvbUFycmF5PWZ1bmN0aW9uKHQpe3ZhciByPW5ldyBlO2lmKCF0Lmxlbmd0aClyZXR1cm4gcjtmb3IodmFyIGk9QXJyYXkuaXNBcnJheSh0WzBdKT8xOjA7aTx0Lmxlbmd0aDsrK2kpe3ZhciBuPXRbaV07aWYoQXJyYXkuaXNBcnJheShuKSlmb3IodmFyIG89MDtvPG4ubGVuZ3RoOysrbylyLmFkZFN1YlBhcmFtKG5bb10pO2Vsc2Ugci5hZGRQYXJhbShuKX1yZXR1cm4gcn0sZS5wcm90b3R5cGUuY2xvbmU9ZnVuY3Rpb24oKXt2YXIgdD1uZXcgZSh0aGlzLm1heExlbmd0aCx0aGlzLm1heFN1YlBhcmFtc0xlbmd0aCk7cmV0dXJuIHQucGFyYW1zLnNldCh0aGlzLnBhcmFtcyksdC5sZW5ndGg9dGhpcy5sZW5ndGgsdC5fc3ViUGFyYW1zLnNldCh0aGlzLl9zdWJQYXJhbXMpLHQuX3N1YlBhcmFtc0xlbmd0aD10aGlzLl9zdWJQYXJhbXNMZW5ndGgsdC5fc3ViUGFyYW1zSWR4LnNldCh0aGlzLl9zdWJQYXJhbXNJZHgpLHQuX3JlamVjdERpZ2l0cz10aGlzLl9yZWplY3REaWdpdHMsdC5fcmVqZWN0U3ViRGlnaXRzPXRoaXMuX3JlamVjdFN1YkRpZ2l0cyx0Ll9kaWdpdElzU3ViPXRoaXMuX2RpZ2l0SXNTdWIsdH0sZS5wcm90b3R5cGUudG9BcnJheT1mdW5jdGlvbigpe2Zvcih2YXIgZT1bXSx0PTA7dDx0aGlzLmxlbmd0aDsrK3Qpe2UucHVzaCh0aGlzLnBhcmFtc1t0XSk7dmFyIHI9dGhpcy5fc3ViUGFyYW1zSWR4W3RdPj44LGk9MjU1JnRoaXMuX3N1YlBhcmFtc0lkeFt0XTtpLXI+MCYmZS5wdXNoKEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHRoaXMuX3N1YlBhcmFtcyxyLGkpKX1yZXR1cm4gZX0sZS5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLmxlbmd0aD0wLHRoaXMuX3N1YlBhcmFtc0xlbmd0aD0wLHRoaXMuX3JlamVjdERpZ2l0cz0hMSx0aGlzLl9yZWplY3RTdWJEaWdpdHM9ITEsdGhpcy5fZGlnaXRJc1N1Yj0hMX0sZS5wcm90b3R5cGUuYWRkUGFyYW09ZnVuY3Rpb24oZSl7aWYodGhpcy5fZGlnaXRJc1N1Yj0hMSx0aGlzLmxlbmd0aD49dGhpcy5tYXhMZW5ndGgpdGhpcy5fcmVqZWN0RGlnaXRzPSEwO2Vsc2V7aWYoZTwtMSl0aHJvdyBuZXcgRXJyb3IoInZhbHVlcyBsZXNzZXIgdGhhbiAtMSBhcmUgbm90IGFsbG93ZWQiKTt0aGlzLl9zdWJQYXJhbXNJZHhbdGhpcy5sZW5ndGhdPXRoaXMuX3N1YlBhcmFtc0xlbmd0aDw8OHx0aGlzLl9zdWJQYXJhbXNMZW5ndGgsdGhpcy5wYXJhbXNbdGhpcy5sZW5ndGgrK109ZT5yP3I6ZX19LGUucHJvdG90eXBlLmFkZFN1YlBhcmFtPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2RpZ2l0SXNTdWI9ITAsdGhpcy5sZW5ndGgpaWYodGhpcy5fcmVqZWN0RGlnaXRzfHx0aGlzLl9zdWJQYXJhbXNMZW5ndGg+PXRoaXMubWF4U3ViUGFyYW1zTGVuZ3RoKXRoaXMuX3JlamVjdFN1YkRpZ2l0cz0hMDtlbHNle2lmKGU8LTEpdGhyb3cgbmV3IEVycm9yKCJ2YWx1ZXMgbGVzc2VyIHRoYW4gLTEgYXJlIG5vdCBhbGxvd2VkIik7dGhpcy5fc3ViUGFyYW1zW3RoaXMuX3N1YlBhcmFtc0xlbmd0aCsrXT1lPnI/cjplLHRoaXMuX3N1YlBhcmFtc0lkeFt0aGlzLmxlbmd0aC0xXSsrfX0sZS5wcm90b3R5cGUuaGFzU3ViUGFyYW1zPWZ1bmN0aW9uKGUpe3JldHVybigyNTUmdGhpcy5fc3ViUGFyYW1zSWR4W2VdKS0odGhpcy5fc3ViUGFyYW1zSWR4W2VdPj44KT4wfSxlLnByb3RvdHlwZS5nZXRTdWJQYXJhbXM9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fc3ViUGFyYW1zSWR4W2VdPj44LHI9MjU1JnRoaXMuX3N1YlBhcmFtc0lkeFtlXTtyZXR1cm4gci10PjA/dGhpcy5fc3ViUGFyYW1zLnN1YmFycmF5KHQscik6bnVsbH0sZS5wcm90b3R5cGUuZ2V0U3ViUGFyYW1zQWxsPWZ1bmN0aW9uKCl7Zm9yKHZhciBlPXt9LHQ9MDt0PHRoaXMubGVuZ3RoOysrdCl7dmFyIHI9dGhpcy5fc3ViUGFyYW1zSWR4W3RdPj44LGk9MjU1JnRoaXMuX3N1YlBhcmFtc0lkeFt0XTtpLXI+MCYmKGVbdF09dGhpcy5fc3ViUGFyYW1zLnNsaWNlKHIsaSkpfXJldHVybiBlfSxlLnByb3RvdHlwZS5hZGREaWdpdD1mdW5jdGlvbihlKXt2YXIgdDtpZighKHRoaXMuX3JlamVjdERpZ2l0c3x8ISh0PXRoaXMuX2RpZ2l0SXNTdWI/dGhpcy5fc3ViUGFyYW1zTGVuZ3RoOnRoaXMubGVuZ3RoKXx8dGhpcy5fZGlnaXRJc1N1YiYmdGhpcy5fcmVqZWN0U3ViRGlnaXRzKSl7dmFyIGk9dGhpcy5fZGlnaXRJc1N1Yj90aGlzLl9zdWJQYXJhbXM6dGhpcy5wYXJhbXMsbj1pW3QtMV07aVt0LTFdPX5uP01hdGgubWluKDEwKm4rZSxyKTplfX0sZX0oKTt0LlBhcmFtcz1pfSw1NzQxOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQWRkb25NYW5hZ2VyPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt0aGlzLl9hZGRvbnM9W119cmV0dXJuIGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXtmb3IodmFyIGU9dGhpcy5fYWRkb25zLmxlbmd0aC0xO2U+PTA7ZS0tKXRoaXMuX2FkZG9uc1tlXS5pbnN0YW5jZS5kaXNwb3NlKCl9LGUucHJvdG90eXBlLmxvYWRBZGRvbj1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMsaT17aW5zdGFuY2U6dCxkaXNwb3NlOnQuZGlzcG9zZSxpc0Rpc3Bvc2VkOiExfTt0aGlzLl9hZGRvbnMucHVzaChpKSx0LmRpc3Bvc2U9ZnVuY3Rpb24oKXtyZXR1cm4gci5fd3JhcHBlZEFkZG9uRGlzcG9zZShpKX0sdC5hY3RpdmF0ZShlKX0sZS5wcm90b3R5cGUuX3dyYXBwZWRBZGRvbkRpc3Bvc2U9ZnVuY3Rpb24oZSl7aWYoIWUuaXNEaXNwb3NlZCl7Zm9yKHZhciB0PS0xLHI9MDtyPHRoaXMuX2FkZG9ucy5sZW5ndGg7cisrKWlmKHRoaXMuX2FkZG9uc1tyXT09PWUpe3Q9cjticmVha31pZigtMT09PXQpdGhyb3cgbmV3IEVycm9yKCJDb3VsZCBub3QgZGlzcG9zZSBhbiBhZGRvbiB0aGF0IGhhcyBub3QgYmVlbiBsb2FkZWQiKTtlLmlzRGlzcG9zZWQ9ITAsZS5kaXNwb3NlLmFwcGx5KGUuaW5zdGFuY2UpLHRoaXMuX2FkZG9ucy5zcGxpY2UodCwxKX19LGV9KCk7dC5BZGRvbk1hbmFnZXI9cn0sODc3MTooZSx0LHIpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQnVmZmVyQXBpVmlldz12b2lkIDA7dmFyIGk9cigzNzg1KSxuPXIoNTExKSxvPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQpe3RoaXMuX2J1ZmZlcj1lLHRoaXMudHlwZT10fXJldHVybiBlLnByb3RvdHlwZS5pbml0PWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9idWZmZXI9ZSx0aGlzfSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImN1cnNvclkiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYnVmZmVyLnl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJjdXJzb3JYIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2J1ZmZlci54fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwidmlld3BvcnRZIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2J1ZmZlci55ZGlzcH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImJhc2VZIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2J1ZmZlci55YmFzZX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImxlbmd0aCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9idWZmZXIubGluZXMubGVuZ3RofSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmdldExpbmU9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fYnVmZmVyLmxpbmVzLmdldChlKTtpZih0KXJldHVybiBuZXcgaS5CdWZmZXJMaW5lQXBpVmlldyh0KX0sZS5wcm90b3R5cGUuZ2V0TnVsbENlbGw9ZnVuY3Rpb24oKXtyZXR1cm4gbmV3IG4uQ2VsbERhdGF9LGV9KCk7dC5CdWZmZXJBcGlWaWV3PW99LDM3ODU6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJ1ZmZlckxpbmVBcGlWaWV3PXZvaWQgMDt2YXIgaT1yKDUxMSksbj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fbGluZT1lfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImlzV3JhcHBlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9saW5lLmlzV3JhcHBlZH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImxlbmd0aCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9saW5lLmxlbmd0aH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5nZXRDZWxsPWZ1bmN0aW9uKGUsdCl7aWYoIShlPDB8fGU+PXRoaXMuX2xpbmUubGVuZ3RoKSlyZXR1cm4gdD8odGhpcy5fbGluZS5sb2FkQ2VsbChlLHQpLHQpOnRoaXMuX2xpbmUubG9hZENlbGwoZSxuZXcgaS5DZWxsRGF0YSl9LGUucHJvdG90eXBlLnRyYW5zbGF0ZVRvU3RyaW5nPWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gdGhpcy5fbGluZS50cmFuc2xhdGVUb1N0cmluZyhlLHQscil9LGV9KCk7dC5CdWZmZXJMaW5lQXBpVmlldz1ufSw4Mjg1OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5CdWZmZXJOYW1lc3BhY2VBcGk9dm9pZCAwO3ZhciBpPXIoODc3MSksbj1yKDg0NjApLG89ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe3ZhciB0PXRoaXM7dGhpcy5fY29yZT1lLHRoaXMuX29uQnVmZmVyQ2hhbmdlPW5ldyBuLkV2ZW50RW1pdHRlcix0aGlzLl9ub3JtYWw9bmV3IGkuQnVmZmVyQXBpVmlldyh0aGlzLl9jb3JlLmJ1ZmZlcnMubm9ybWFsLCJub3JtYWwiKSx0aGlzLl9hbHRlcm5hdGU9bmV3IGkuQnVmZmVyQXBpVmlldyh0aGlzLl9jb3JlLmJ1ZmZlcnMuYWx0LCJhbHRlcm5hdGUiKSx0aGlzLl9jb3JlLmJ1ZmZlcnMub25CdWZmZXJBY3RpdmF0ZSgoZnVuY3Rpb24oKXtyZXR1cm4gdC5fb25CdWZmZXJDaGFuZ2UuZmlyZSh0LmFjdGl2ZSl9KSl9cmV0dXJuIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25CdWZmZXJDaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25CdWZmZXJDaGFuZ2UuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJhY3RpdmUiLHtnZXQ6ZnVuY3Rpb24oKXtpZih0aGlzLl9jb3JlLmJ1ZmZlcnMuYWN0aXZlPT09dGhpcy5fY29yZS5idWZmZXJzLm5vcm1hbClyZXR1cm4gdGhpcy5ub3JtYWw7aWYodGhpcy5fY29yZS5idWZmZXJzLmFjdGl2ZT09PXRoaXMuX2NvcmUuYnVmZmVycy5hbHQpcmV0dXJuIHRoaXMuYWx0ZXJuYXRlO3Rocm93IG5ldyBFcnJvcigiQWN0aXZlIGJ1ZmZlciBpcyBuZWl0aGVyIG5vcm1hbCBub3IgYWx0ZXJuYXRlIil9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJub3JtYWwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fbm9ybWFsLmluaXQodGhpcy5fY29yZS5idWZmZXJzLm5vcm1hbCl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJhbHRlcm5hdGUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYWx0ZXJuYXRlLmluaXQodGhpcy5fY29yZS5idWZmZXJzLmFsdCl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZX0oKTt0LkJ1ZmZlck5hbWVzcGFjZUFwaT1vfSw3OTc1OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuUGFyc2VyQXBpPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fY29yZT1lfXJldHVybiBlLnByb3RvdHlwZS5yZWdpc3RlckNzaUhhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5fY29yZS5yZWdpc3RlckNzaUhhbmRsZXIoZSwoZnVuY3Rpb24oZSl7cmV0dXJuIHQoZS50b0FycmF5KCkpfSkpfSxlLnByb3RvdHlwZS5hZGRDc2lIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMucmVnaXN0ZXJDc2lIYW5kbGVyKGUsdCl9LGUucHJvdG90eXBlLnJlZ2lzdGVyRGNzSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9jb3JlLnJlZ2lzdGVyRGNzSGFuZGxlcihlLChmdW5jdGlvbihlLHIpe3JldHVybiB0KGUsci50b0FycmF5KCkpfSkpfSxlLnByb3RvdHlwZS5hZGREY3NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMucmVnaXN0ZXJEY3NIYW5kbGVyKGUsdCl9LGUucHJvdG90eXBlLnJlZ2lzdGVyRXNjSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9jb3JlLnJlZ2lzdGVyRXNjSGFuZGxlcihlLHQpfSxlLnByb3RvdHlwZS5hZGRFc2NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMucmVnaXN0ZXJFc2NIYW5kbGVyKGUsdCl9LGUucHJvdG90eXBlLnJlZ2lzdGVyT3NjSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9jb3JlLnJlZ2lzdGVyT3NjSGFuZGxlcihlLHQpfSxlLnByb3RvdHlwZS5hZGRPc2NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMucmVnaXN0ZXJPc2NIYW5kbGVyKGUsdCl9LGV9KCk7dC5QYXJzZXJBcGk9cn0sNzA5MDooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlVuaWNvZGVBcGk9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9jb3JlPWV9cmV0dXJuIGUucHJvdG90eXBlLnJlZ2lzdGVyPWZ1bmN0aW9uKGUpe3RoaXMuX2NvcmUudW5pY29kZVNlcnZpY2UucmVnaXN0ZXIoZSl9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwidmVyc2lvbnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS51bmljb2RlU2VydmljZS52ZXJzaW9uc30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImFjdGl2ZVZlcnNpb24iLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS51bmljb2RlU2VydmljZS5hY3RpdmVWZXJzaW9ufSxzZXQ6ZnVuY3Rpb24oZSl7dGhpcy5fY29yZS51bmljb2RlU2VydmljZS5hY3RpdmVWZXJzaW9uPWV9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZX0oKTt0LlVuaWNvZGVBcGk9cn0sNzQ0OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJ1ZmZlclNlcnZpY2U9dC5NSU5JTVVNX1JPV1M9dC5NSU5JTVVNX0NPTFM9dm9pZCAwO3ZhciBhPXIoMjU4NSksYz1yKDUyOTUpLGw9cig4NDYwKSx1PXIoODQ0KTt0Lk1JTklNVU1fQ09MUz0yLHQuTUlOSU1VTV9ST1dTPTE7dmFyIGg9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gcihyKXt2YXIgaT1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIGkuX29wdGlvbnNTZXJ2aWNlPXIsaS5pc1VzZXJTY3JvbGxpbmc9ITEsaS5fb25SZXNpemU9bmV3IGwuRXZlbnRFbWl0dGVyLGkuX29uU2Nyb2xsPW5ldyBsLkV2ZW50RW1pdHRlcixpLmNvbHM9TWF0aC5tYXgoci5vcHRpb25zLmNvbHN8fDAsdC5NSU5JTVVNX0NPTFMpLGkucm93cz1NYXRoLm1heChyLm9wdGlvbnMucm93c3x8MCx0Lk1JTklNVU1fUk9XUyksaS5idWZmZXJzPW5ldyBjLkJ1ZmZlclNldChyLGkpLGl9cmV0dXJuIG4ocixlKSxPYmplY3QuZGVmaW5lUHJvcGVydHkoci5wcm90b3R5cGUsIm9uUmVzaXplIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uUmVzaXplLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyLnByb3RvdHlwZSwib25TY3JvbGwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25TY3JvbGwuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHIucHJvdG90eXBlLCJidWZmZXIiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5idWZmZXJzLmFjdGl2ZX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxyLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7ZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpLHRoaXMuYnVmZmVycy5kaXNwb3NlKCl9LHIucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuY29scz1lLHRoaXMucm93cz10LHRoaXMuYnVmZmVycy5yZXNpemUoZSx0KSx0aGlzLmJ1ZmZlcnMuc2V0dXBUYWJTdG9wcyh0aGlzLmNvbHMpLHRoaXMuX29uUmVzaXplLmZpcmUoe2NvbHM6ZSxyb3dzOnR9KX0sci5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLmJ1ZmZlcnMucmVzZXQoKSx0aGlzLmlzVXNlclNjcm9sbGluZz0hMX0sci5wcm90b3R5cGUuc2Nyb2xsPWZ1bmN0aW9uKGUsdCl7dm9pZCAwPT09dCYmKHQ9ITEpO3ZhciByLGk9dGhpcy5idWZmZXI7KHI9dGhpcy5fY2FjaGVkQmxhbmtMaW5lKSYmci5sZW5ndGg9PT10aGlzLmNvbHMmJnIuZ2V0RmcoMCk9PT1lLmZnJiZyLmdldEJnKDApPT09ZS5iZ3x8KHI9aS5nZXRCbGFua0xpbmUoZSx0KSx0aGlzLl9jYWNoZWRCbGFua0xpbmU9ciksci5pc1dyYXBwZWQ9dDt2YXIgbj1pLnliYXNlK2kuc2Nyb2xsVG9wLG89aS55YmFzZStpLnNjcm9sbEJvdHRvbTtpZigwPT09aS5zY3JvbGxUb3Ape3ZhciBzPWkubGluZXMuaXNGdWxsO289PT1pLmxpbmVzLmxlbmd0aC0xP3M/aS5saW5lcy5yZWN5Y2xlKCkuY29weUZyb20ocik6aS5saW5lcy5wdXNoKHIuY2xvbmUoKSk6aS5saW5lcy5zcGxpY2UobysxLDAsci5jbG9uZSgpKSxzP3RoaXMuaXNVc2VyU2Nyb2xsaW5nJiYoaS55ZGlzcD1NYXRoLm1heChpLnlkaXNwLTEsMCkpOihpLnliYXNlKyssdGhpcy5pc1VzZXJTY3JvbGxpbmd8fGkueWRpc3ArKyl9ZWxzZXt2YXIgYT1vLW4rMTtpLmxpbmVzLnNoaWZ0RWxlbWVudHMobisxLGEtMSwtMSksaS5saW5lcy5zZXQobyxyLmNsb25lKCkpfXRoaXMuaXNVc2VyU2Nyb2xsaW5nfHwoaS55ZGlzcD1pLnliYXNlKSx0aGlzLl9vblNjcm9sbC5maXJlKGkueWRpc3ApfSxyLnByb3RvdHlwZS5zY3JvbGxMaW5lcz1mdW5jdGlvbihlLHQscil7dmFyIGk9dGhpcy5idWZmZXI7aWYoZTwwKXtpZigwPT09aS55ZGlzcClyZXR1cm47dGhpcy5pc1VzZXJTY3JvbGxpbmc9ITB9ZWxzZSBlK2kueWRpc3A+PWkueWJhc2UmJih0aGlzLmlzVXNlclNjcm9sbGluZz0hMSk7dmFyIG49aS55ZGlzcDtpLnlkaXNwPU1hdGgubWF4KE1hdGgubWluKGkueWRpc3ArZSxpLnliYXNlKSwwKSxuIT09aS55ZGlzcCYmKHR8fHRoaXMuX29uU2Nyb2xsLmZpcmUoaS55ZGlzcCkpfSxyLnByb3RvdHlwZS5zY3JvbGxQYWdlcz1mdW5jdGlvbihlKXt0aGlzLnNjcm9sbExpbmVzKGUqKHRoaXMucm93cy0xKSl9LHIucHJvdG90eXBlLnNjcm9sbFRvVG9wPWZ1bmN0aW9uKCl7dGhpcy5zY3JvbGxMaW5lcygtdGhpcy5idWZmZXIueWRpc3ApfSxyLnByb3RvdHlwZS5zY3JvbGxUb0JvdHRvbT1mdW5jdGlvbigpe3RoaXMuc2Nyb2xsTGluZXModGhpcy5idWZmZXIueWJhc2UtdGhpcy5idWZmZXIueWRpc3ApfSxyLnByb3RvdHlwZS5zY3JvbGxUb0xpbmU9ZnVuY3Rpb24oZSl7dmFyIHQ9ZS10aGlzLmJ1ZmZlci55ZGlzcDswIT09dCYmdGhpcy5zY3JvbGxMaW5lcyh0KX0sbyhbcygwLGEuSU9wdGlvbnNTZXJ2aWNlKV0scil9KHUuRGlzcG9zYWJsZSk7dC5CdWZmZXJTZXJ2aWNlPWh9LDc5OTQ6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5DaGFyc2V0U2VydmljZT12b2lkIDA7dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKCl7dGhpcy5nbGV2ZWw9MCx0aGlzLl9jaGFyc2V0cz1bXX1yZXR1cm4gZS5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLmNoYXJzZXQ9dm9pZCAwLHRoaXMuX2NoYXJzZXRzPVtdLHRoaXMuZ2xldmVsPTB9LGUucHJvdG90eXBlLnNldGdMZXZlbD1mdW5jdGlvbihlKXt0aGlzLmdsZXZlbD1lLHRoaXMuY2hhcnNldD10aGlzLl9jaGFyc2V0c1tlXX0sZS5wcm90b3R5cGUuc2V0Z0NoYXJzZXQ9ZnVuY3Rpb24oZSx0KXt0aGlzLl9jaGFyc2V0c1tlXT10LHRoaXMuZ2xldmVsPT09ZSYmKHRoaXMuY2hhcnNldD10KX0sZX0oKTt0LkNoYXJzZXRTZXJ2aWNlPXJ9LDE3NTM6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Db3JlTW91c2VTZXJ2aWNlPXZvaWQgMDt2YXIgbz1yKDI1ODUpLHM9cig4NDYwKSxhPXtOT05FOntldmVudHM6MCxyZXN0cmljdDpmdW5jdGlvbigpe3JldHVybiExfX0sWDEwOntldmVudHM6MSxyZXN0cmljdDpmdW5jdGlvbihlKXtyZXR1cm4gNCE9PWUuYnV0dG9uJiYxPT09ZS5hY3Rpb24mJihlLmN0cmw9ITEsZS5hbHQ9ITEsZS5zaGlmdD0hMSwhMCl9fSxWVDIwMDp7ZXZlbnRzOjE5LHJlc3RyaWN0OmZ1bmN0aW9uKGUpe3JldHVybiAzMiE9PWUuYWN0aW9ufX0sRFJBRzp7ZXZlbnRzOjIzLHJlc3RyaWN0OmZ1bmN0aW9uKGUpe3JldHVybiAzMiE9PWUuYWN0aW9ufHwzIT09ZS5idXR0b259fSxBTlk6e2V2ZW50czozMSxyZXN0cmljdDpmdW5jdGlvbihlKXtyZXR1cm4hMH19fTtmdW5jdGlvbiBjKGUsdCl7dmFyIHI9KGUuY3RybD8xNjowKXwoZS5zaGlmdD80OjApfChlLmFsdD84OjApO3JldHVybiA0PT09ZS5idXR0b24/KHJ8PTY0LHJ8PWUuYWN0aW9uKToocnw9MyZlLmJ1dHRvbiw0JmUuYnV0dG9uJiYocnw9NjQpLDgmZS5idXR0b24mJihyfD0xMjgpLDMyPT09ZS5hY3Rpb24/cnw9MzI6MCE9PWUuYWN0aW9ufHx0fHwocnw9MykpLHJ9dmFyIGw9U3RyaW5nLmZyb21DaGFyQ29kZSx1PXtERUZBVUxUOmZ1bmN0aW9uKGUpe3ZhciB0PVtjKGUsITEpKzMyLGUuY29sKzMyLGUucm93KzMyXTtyZXR1cm4gdFswXT4yNTV8fHRbMV0+MjU1fHx0WzJdPjI1NT8iIjoiG1tNIitsKHRbMF0pK2wodFsxXSkrbCh0WzJdKX0sU0dSOmZ1bmN0aW9uKGUpe3ZhciB0PTA9PT1lLmFjdGlvbiYmNCE9PWUuYnV0dG9uPyJtIjoiTSI7cmV0dXJuIhtbPCIrYyhlLCEwKSsiOyIrZS5jb2wrIjsiK2Uucm93K3R9fSxoPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQpe3RoaXMuX2J1ZmZlclNlcnZpY2U9ZSx0aGlzLl9jb3JlU2VydmljZT10LHRoaXMuX3Byb3RvY29scz17fSx0aGlzLl9lbmNvZGluZ3M9e30sdGhpcy5fYWN0aXZlUHJvdG9jb2w9IiIsdGhpcy5fYWN0aXZlRW5jb2Rpbmc9IiIsdGhpcy5fb25Qcm90b2NvbENoYW5nZT1uZXcgcy5FdmVudEVtaXR0ZXIsdGhpcy5fbGFzdEV2ZW50PW51bGw7Zm9yKHZhciByPTAsaT1PYmplY3Qua2V5cyhhKTtyPGkubGVuZ3RoO3IrKyl7dmFyIG49aVtyXTt0aGlzLmFkZFByb3RvY29sKG4sYVtuXSl9Zm9yKHZhciBvPTAsYz1PYmplY3Qua2V5cyh1KTtvPGMubGVuZ3RoO28rKyl7dmFyIGw9Y1tvXTt0aGlzLmFkZEVuY29kaW5nKGwsdVtsXSl9dGhpcy5yZXNldCgpfXJldHVybiBlLnByb3RvdHlwZS5hZGRQcm90b2NvbD1mdW5jdGlvbihlLHQpe3RoaXMuX3Byb3RvY29sc1tlXT10fSxlLnByb3RvdHlwZS5hZGRFbmNvZGluZz1mdW5jdGlvbihlLHQpe3RoaXMuX2VuY29kaW5nc1tlXT10fSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImFjdGl2ZVByb3RvY29sIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2FjdGl2ZVByb3RvY29sfSxzZXQ6ZnVuY3Rpb24oZSl7aWYoIXRoaXMuX3Byb3RvY29sc1tlXSl0aHJvdyBuZXcgRXJyb3IoJ3Vua25vd24gcHJvdG9jb2wgIicrZSsnIicpO3RoaXMuX2FjdGl2ZVByb3RvY29sPWUsdGhpcy5fb25Qcm90b2NvbENoYW5nZS5maXJlKHRoaXMuX3Byb3RvY29sc1tlXS5ldmVudHMpfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiYXJlTW91c2VFdmVudHNBY3RpdmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gMCE9PXRoaXMuX3Byb3RvY29sc1t0aGlzLl9hY3RpdmVQcm90b2NvbF0uZXZlbnRzfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiYWN0aXZlRW5jb2RpbmciLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYWN0aXZlRW5jb2Rpbmd9LHNldDpmdW5jdGlvbihlKXtpZighdGhpcy5fZW5jb2RpbmdzW2VdKXRocm93IG5ldyBFcnJvcigndW5rbm93biBlbmNvZGluZyAiJytlKyciJyk7dGhpcy5fYWN0aXZlRW5jb2Rpbmc9ZX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe3RoaXMuYWN0aXZlUHJvdG9jb2w9Ik5PTkUiLHRoaXMuYWN0aXZlRW5jb2Rpbmc9IkRFRkFVTFQiLHRoaXMuX2xhc3RFdmVudD1udWxsfSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uUHJvdG9jb2xDaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25Qcm90b2NvbENoYW5nZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS50cmlnZ2VyTW91c2VFdmVudD1mdW5jdGlvbihlKXtpZihlLmNvbDwwfHxlLmNvbD49dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzfHxlLnJvdzwwfHxlLnJvdz49dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKXJldHVybiExO2lmKDQ9PT1lLmJ1dHRvbiYmMzI9PT1lLmFjdGlvbilyZXR1cm4hMTtpZigzPT09ZS5idXR0b24mJjMyIT09ZS5hY3Rpb24pcmV0dXJuITE7aWYoNCE9PWUuYnV0dG9uJiYoMj09PWUuYWN0aW9ufHwzPT09ZS5hY3Rpb24pKXJldHVybiExO2lmKGUuY29sKyssZS5yb3crKywzMj09PWUuYWN0aW9uJiZ0aGlzLl9sYXN0RXZlbnQmJnRoaXMuX2NvbXBhcmVFdmVudHModGhpcy5fbGFzdEV2ZW50LGUpKXJldHVybiExO2lmKCF0aGlzLl9wcm90b2NvbHNbdGhpcy5fYWN0aXZlUHJvdG9jb2xdLnJlc3RyaWN0KGUpKXJldHVybiExO3ZhciB0PXRoaXMuX2VuY29kaW5nc1t0aGlzLl9hY3RpdmVFbmNvZGluZ10oZSk7cmV0dXJuIHQmJigiREVGQVVMVCI9PT10aGlzLl9hY3RpdmVFbmNvZGluZz90aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyQmluYXJ5RXZlbnQodCk6dGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudCh0LCEwKSksdGhpcy5fbGFzdEV2ZW50PWUsITB9LGUucHJvdG90eXBlLmV4cGxhaW5FdmVudHM9ZnVuY3Rpb24oZSl7cmV0dXJue2Rvd246ISEoMSZlKSx1cDohISgyJmUpLGRyYWc6ISEoNCZlKSxtb3ZlOiEhKDgmZSksd2hlZWw6ISEoMTYmZSl9fSxlLnByb3RvdHlwZS5fY29tcGFyZUV2ZW50cz1mdW5jdGlvbihlLHQpe3JldHVybiBlLmNvbD09PXQuY29sJiZlLnJvdz09PXQucm93JiZlLmJ1dHRvbj09PXQuYnV0dG9uJiZlLmFjdGlvbj09PXQuYWN0aW9uJiZlLmN0cmw9PT10LmN0cmwmJmUuYWx0PT09dC5hbHQmJmUuc2hpZnQ9PT10LnNoaWZ0fSxpKFtuKDAsby5JQnVmZmVyU2VydmljZSksbigxLG8uSUNvcmVTZXJ2aWNlKV0sZSl9KCk7dC5Db3JlTW91c2VTZXJ2aWNlPWh9LDY5NzU6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSksbz10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LHM9dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQ29yZVNlcnZpY2U9dm9pZCAwO3ZhciBhPXIoMjU4NSksYz1yKDg0NjApLGw9cigxNDM5KSx1PXIoODQ0KSxoPU9iamVjdC5mcmVlemUoe2luc2VydE1vZGU6ITF9KSxmPU9iamVjdC5mcmVlemUoe2FwcGxpY2F0aW9uQ3Vyc29yS2V5czohMSxhcHBsaWNhdGlvbktleXBhZDohMSxicmFja2V0ZWRQYXN0ZU1vZGU6ITEsb3JpZ2luOiExLHJldmVyc2VXcmFwYXJvdW5kOiExLHNlbmRGb2N1czohMSx3cmFwYXJvdW5kOiEwfSksXz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpLG4pe3ZhciBvPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gby5fYnVmZmVyU2VydmljZT1yLG8uX2xvZ1NlcnZpY2U9aSxvLl9vcHRpb25zU2VydmljZT1uLG8uaXNDdXJzb3JJbml0aWFsaXplZD0hMSxvLmlzQ3Vyc29ySGlkZGVuPSExLG8uX29uRGF0YT1vLnJlZ2lzdGVyKG5ldyBjLkV2ZW50RW1pdHRlciksby5fb25Vc2VySW5wdXQ9by5yZWdpc3RlcihuZXcgYy5FdmVudEVtaXR0ZXIpLG8uX29uQmluYXJ5PW8ucmVnaXN0ZXIobmV3IGMuRXZlbnRFbWl0dGVyKSxvLl9zY3JvbGxUb0JvdHRvbT10LG8ucmVnaXN0ZXIoe2Rpc3Bvc2U6ZnVuY3Rpb24oKXtyZXR1cm4gby5fc2Nyb2xsVG9Cb3R0b209dm9pZCAwfX0pLG8ubW9kZXM9KDAsbC5jbG9uZSkoaCksby5kZWNQcml2YXRlTW9kZXM9KDAsbC5jbG9uZSkoZiksb31yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25EYXRhIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uRGF0YS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uVXNlcklucHV0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uVXNlcklucHV0LmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25CaW5hcnkiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25CaW5hcnkuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLm1vZGVzPSgwLGwuY2xvbmUpKGgpLHRoaXMuZGVjUHJpdmF0ZU1vZGVzPSgwLGwuY2xvbmUpKGYpfSx0LnByb3RvdHlwZS50cmlnZ2VyRGF0YUV2ZW50PWZ1bmN0aW9uKGUsdCl7aWYodm9pZCAwPT09dCYmKHQ9ITEpLCF0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmRpc2FibGVTdGRpbil7dmFyIHI9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXI7ci55YmFzZSE9PXIueWRpc3AmJnRoaXMuX3Njcm9sbFRvQm90dG9tKCksdCYmdGhpcy5fb25Vc2VySW5wdXQuZmlyZSgpLHRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoJ3NlbmRpbmcgZGF0YSAiJytlKyciJywoZnVuY3Rpb24oKXtyZXR1cm4gZS5zcGxpdCgiIikubWFwKChmdW5jdGlvbihlKXtyZXR1cm4gZS5jaGFyQ29kZUF0KDApfSkpfSkpLHRoaXMuX29uRGF0YS5maXJlKGUpfX0sdC5wcm90b3R5cGUudHJpZ2dlckJpbmFyeUV2ZW50PWZ1bmN0aW9uKGUpe3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZGlzYWJsZVN0ZGlufHwodGhpcy5fbG9nU2VydmljZS5kZWJ1Zygnc2VuZGluZyBiaW5hcnkgIicrZSsnIicsKGZ1bmN0aW9uKCl7cmV0dXJuIGUuc3BsaXQoIiIpLm1hcCgoZnVuY3Rpb24oZSl7cmV0dXJuIGUuY2hhckNvZGVBdCgwKX0pKX0pKSx0aGlzLl9vbkJpbmFyeS5maXJlKGUpKX0sbyhbcygxLGEuSUJ1ZmZlclNlcnZpY2UpLHMoMixhLklMb2dTZXJ2aWNlKSxzKDMsYS5JT3B0aW9uc1NlcnZpY2UpXSx0KX0odS5EaXNwb3NhYmxlKTt0LkNvcmVTZXJ2aWNlPV99LDM3MzA6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5EaXJ0eVJvd1NlcnZpY2U9dm9pZCAwO3ZhciBvPXIoMjU4NSkscz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fYnVmZmVyU2VydmljZT1lLHRoaXMuY2xlYXJSYW5nZSgpfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsInN0YXJ0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX3N0YXJ0fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiZW5kIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2VuZH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5jbGVhclJhbmdlPWZ1bmN0aW9uKCl7dGhpcy5fc3RhcnQ9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueSx0aGlzLl9lbmQ9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueX0sZS5wcm90b3R5cGUubWFya0RpcnR5PWZ1bmN0aW9uKGUpe2U8dGhpcy5fc3RhcnQ/dGhpcy5fc3RhcnQ9ZTplPnRoaXMuX2VuZCYmKHRoaXMuX2VuZD1lKX0sZS5wcm90b3R5cGUubWFya1JhbmdlRGlydHk9ZnVuY3Rpb24oZSx0KXtpZihlPnQpe3ZhciByPWU7ZT10LHQ9cn1lPHRoaXMuX3N0YXJ0JiYodGhpcy5fc3RhcnQ9ZSksdD50aGlzLl9lbmQmJih0aGlzLl9lbmQ9dCl9LGUucHJvdG90eXBlLm1hcmtBbGxEaXJ0eT1mdW5jdGlvbigpe3RoaXMubWFya1JhbmdlRGlydHkoMCx0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSl9LGkoW24oMCxvLklCdWZmZXJTZXJ2aWNlKV0sZSl9KCk7dC5EaXJ0eVJvd1NlcnZpY2U9c30sNDM0ODpmdW5jdGlvbihlLHQscil7dmFyIGk9dGhpcyYmdGhpcy5fX3NwcmVhZEFycmF5fHxmdW5jdGlvbihlLHQscil7aWYocnx8Mj09PWFyZ3VtZW50cy5sZW5ndGgpZm9yKHZhciBpLG49MCxvPXQubGVuZ3RoO248bztuKyspIWkmJm4gaW4gdHx8KGl8fChpPUFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHQsMCxuKSksaVtuXT10W25dKTtyZXR1cm4gZS5jb25jYXQoaXx8QXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwodCkpfTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5JbnN0YW50aWF0aW9uU2VydmljZT10LlNlcnZpY2VDb2xsZWN0aW9uPXZvaWQgMDt2YXIgbj1yKDI1ODUpLG89cig4MzQzKSxzPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe2Zvcih2YXIgZT1bXSx0PTA7dDxhcmd1bWVudHMubGVuZ3RoO3QrKyllW3RdPWFyZ3VtZW50c1t0XTt0aGlzLl9lbnRyaWVzPW5ldyBNYXA7Zm9yKHZhciByPTAsaT1lO3I8aS5sZW5ndGg7cisrKXt2YXIgbj1pW3JdLG89blswXSxzPW5bMV07dGhpcy5zZXQobyxzKX19cmV0dXJuIGUucHJvdG90eXBlLnNldD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX2VudHJpZXMuZ2V0KGUpO3JldHVybiB0aGlzLl9lbnRyaWVzLnNldChlLHQpLHJ9LGUucHJvdG90eXBlLmZvckVhY2g9ZnVuY3Rpb24oZSl7dGhpcy5fZW50cmllcy5mb3JFYWNoKChmdW5jdGlvbih0LHIpe3JldHVybiBlKHIsdCl9KSl9LGUucHJvdG90eXBlLmhhcz1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fZW50cmllcy5oYXMoZSl9LGUucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fZW50cmllcy5nZXQoZSl9LGV9KCk7dC5TZXJ2aWNlQ29sbGVjdGlvbj1zO3ZhciBhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX3NlcnZpY2VzPW5ldyBzLHRoaXMuX3NlcnZpY2VzLnNldChuLklJbnN0YW50aWF0aW9uU2VydmljZSx0aGlzKX1yZXR1cm4gZS5wcm90b3R5cGUuc2V0U2VydmljZT1mdW5jdGlvbihlLHQpe3RoaXMuX3NlcnZpY2VzLnNldChlLHQpfSxlLnByb3RvdHlwZS5nZXRTZXJ2aWNlPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9zZXJ2aWNlcy5nZXQoZSl9LGUucHJvdG90eXBlLmNyZWF0ZUluc3RhbmNlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1bXSxyPTE7cjxhcmd1bWVudHMubGVuZ3RoO3IrKyl0W3ItMV09YXJndW1lbnRzW3JdO2Zvcih2YXIgbj0oMCxvLmdldFNlcnZpY2VEZXBlbmRlbmNpZXMpKGUpLnNvcnQoKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUuaW5kZXgtdC5pbmRleH0pKSxzPVtdLGE9MCxjPW47YTxjLmxlbmd0aDthKyspe3ZhciBsPWNbYV0sdT10aGlzLl9zZXJ2aWNlcy5nZXQobC5pZCk7aWYoIXUpdGhyb3cgbmV3IEVycm9yKCJbY3JlYXRlSW5zdGFuY2VdICIrZS5uYW1lKyIgZGVwZW5kcyBvbiBVTktOT1dOIHNlcnZpY2UgIitsLmlkKyIuIik7cy5wdXNoKHUpfXZhciBoPW4ubGVuZ3RoPjA/blswXS5pbmRleDp0Lmxlbmd0aDtpZih0Lmxlbmd0aCE9PWgpdGhyb3cgbmV3IEVycm9yKCJbY3JlYXRlSW5zdGFuY2VdIEZpcnN0IHNlcnZpY2UgZGVwZW5kZW5jeSBvZiAiK2UubmFtZSsiIGF0IHBvc2l0aW9uICIrKGgrMSkrIiBjb25mbGljdHMgd2l0aCAiK3QubGVuZ3RoKyIgc3RhdGljIGFyZ3VtZW50cyIpO3JldHVybiBuZXcoZS5iaW5kLmFwcGx5KGUsaShbdm9pZCAwXSxpKGkoW10sdCwhMCkscywhMCksITEpKSl9LGV9KCk7dC5JbnN0YW50aWF0aW9uU2VydmljZT1hfSw3ODY2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaT10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LG49dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX0sbz10aGlzJiZ0aGlzLl9fc3ByZWFkQXJyYXl8fGZ1bmN0aW9uKGUsdCxyKXtpZihyfHwyPT09YXJndW1lbnRzLmxlbmd0aClmb3IodmFyIGksbj0wLG89dC5sZW5ndGg7bjxvO24rKykhaSYmbiBpbiB0fHwoaXx8KGk9QXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwodCwwLG4pKSxpW25dPXRbbl0pO3JldHVybiBlLmNvbmNhdChpfHxBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCh0KSl9O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkxvZ1NlcnZpY2U9dm9pZCAwO3ZhciBzPXIoMjU4NSksYT17ZGVidWc6cy5Mb2dMZXZlbEVudW0uREVCVUcsaW5mbzpzLkxvZ0xldmVsRW51bS5JTkZPLHdhcm46cy5Mb2dMZXZlbEVudW0uV0FSTixlcnJvcjpzLkxvZ0xldmVsRW51bS5FUlJPUixvZmY6cy5Mb2dMZXZlbEVudW0uT0ZGfSxjPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt2YXIgdD10aGlzO3RoaXMuX29wdGlvbnNTZXJ2aWNlPWUsdGhpcy5sb2dMZXZlbD1zLkxvZ0xldmVsRW51bS5PRkYsdGhpcy5fdXBkYXRlTG9nTGV2ZWwoKSx0aGlzLl9vcHRpb25zU2VydmljZS5vbk9wdGlvbkNoYW5nZSgoZnVuY3Rpb24oZSl7ImxvZ0xldmVsIj09PWUmJnQuX3VwZGF0ZUxvZ0xldmVsKCl9KSl9cmV0dXJuIGUucHJvdG90eXBlLl91cGRhdGVMb2dMZXZlbD1mdW5jdGlvbigpe3RoaXMubG9nTGV2ZWw9YVt0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmxvZ0xldmVsXX0sZS5wcm90b3R5cGUuX2V2YWxMYXp5T3B0aW9uYWxQYXJhbXM9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PTA7dDxlLmxlbmd0aDt0KyspImZ1bmN0aW9uIj09dHlwZW9mIGVbdF0mJihlW3RdPWVbdF0oKSl9LGUucHJvdG90eXBlLl9sb2c9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2V2YWxMYXp5T3B0aW9uYWxQYXJhbXMociksZS5jYWxsLmFwcGx5KGUsbyhbY29uc29sZSwieHRlcm0uanM6ICIrdF0sciwhMSkpfSxlLnByb3RvdHlwZS5kZWJ1Zz1mdW5jdGlvbihlKXtmb3IodmFyIHQ9W10scj0xO3I8YXJndW1lbnRzLmxlbmd0aDtyKyspdFtyLTFdPWFyZ3VtZW50c1tyXTt0aGlzLmxvZ0xldmVsPD1zLkxvZ0xldmVsRW51bS5ERUJVRyYmdGhpcy5fbG9nKGNvbnNvbGUubG9nLGUsdCl9LGUucHJvdG90eXBlLmluZm89ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PVtdLHI9MTtyPGFyZ3VtZW50cy5sZW5ndGg7cisrKXRbci0xXT1hcmd1bWVudHNbcl07dGhpcy5sb2dMZXZlbDw9cy5Mb2dMZXZlbEVudW0uSU5GTyYmdGhpcy5fbG9nKGNvbnNvbGUuaW5mbyxlLHQpfSxlLnByb3RvdHlwZS53YXJuPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1bXSxyPTE7cjxhcmd1bWVudHMubGVuZ3RoO3IrKyl0W3ItMV09YXJndW1lbnRzW3JdO3RoaXMubG9nTGV2ZWw8PXMuTG9nTGV2ZWxFbnVtLldBUk4mJnRoaXMuX2xvZyhjb25zb2xlLndhcm4sZSx0KX0sZS5wcm90b3R5cGUuZXJyb3I9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PVtdLHI9MTtyPGFyZ3VtZW50cy5sZW5ndGg7cisrKXRbci0xXT1hcmd1bWVudHNbcl07dGhpcy5sb2dMZXZlbDw9cy5Mb2dMZXZlbEVudW0uRVJST1ImJnRoaXMuX2xvZyhjb25zb2xlLmVycm9yLGUsdCl9LGkoW24oMCxzLklPcHRpb25zU2VydmljZSldLGUpfSgpO3QuTG9nU2VydmljZT1jfSw3MzAyOmZ1bmN0aW9uKGUsdCxyKXt2YXIgaT10aGlzJiZ0aGlzLl9fYXNzaWdufHxmdW5jdGlvbigpe3JldHVybiBpPU9iamVjdC5hc3NpZ258fGZ1bmN0aW9uKGUpe2Zvcih2YXIgdCxyPTEsaT1hcmd1bWVudHMubGVuZ3RoO3I8aTtyKyspZm9yKHZhciBuIGluIHQ9YXJndW1lbnRzW3JdKU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LG4pJiYoZVtuXT10W25dKTtyZXR1cm4gZX0saS5hcHBseSh0aGlzLGFyZ3VtZW50cyl9O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0Lk9wdGlvbnNTZXJ2aWNlPXQuREVGQVVMVF9PUFRJT05TPXQuREVGQVVMVF9CRUxMX1NPVU5EPXZvaWQgMDt2YXIgbj1yKDg0NjApLG89cig2MTE0KTt0LkRFRkFVTFRfQkVMTF9TT1VORD0iZGF0YTphdWRpby9tcDM7YmFzZTY0LFNVUXpCQUFBQUFBQUkxUlRVMFVBQUFBUEFBQURUR0YyWmpVNExqTXlMakV3TkFBQUFBQUFBQUFBQUFBQS8vdFF4QUFEQjhBaFNteGhJSUVWQ1NpSnJEQ1FCVGN1M1VyQUl3VWRrUmdRYkZBWkMxQ1FFd1RKOW1qUnZCQTRVT0xEOG5LVk9XZmgrVWxLM3ovMTc3T1hyZk9kS2w3cHluM1hmLy9XcmV5VFJVb0FXZ0Jna09BR2JaSEJnRzFPRjZ6TTgyRFdiWmFVbU1CcHRnUWhHanN5WXFjOWFlOVhGejI4MDk0OE5NQldJbmxqeXpzTlJGTFBXZG5aR1dyZGREc2pLMXVudVNyVk45akpzSzhLdVF0UUN0TUJqQ0V0SW1JU2ROS0pPb3BJcEJGcE5TTWJJSENTUnBSUjVpYWtqVGl5ekxoY2hVVUJ3Q2d5S2l3ZUJ2LzdVc1FiZzhpc1ZOb01QTWpBQUFBMGdBQUFCRVZGR21ncUsvLy8vOWJQLzZYQ3lreEJUVVV6TGpFd01LcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXEiLHQuREVGQVVMVF9PUFRJT05TPXtjb2xzOjgwLHJvd3M6MjQsY3Vyc29yQmxpbms6ITEsY3Vyc29yU3R5bGU6ImJsb2NrIixjdXJzb3JXaWR0aDoxLGN1c3RvbUdseXBoczohMCxiZWxsU291bmQ6dC5ERUZBVUxUX0JFTExfU09VTkQsYmVsbFN0eWxlOiJub25lIixkcmF3Qm9sZFRleHRJbkJyaWdodENvbG9yczohMCxmYXN0U2Nyb2xsTW9kaWZpZXI6ImFsdCIsZmFzdFNjcm9sbFNlbnNpdGl2aXR5OjUsZm9udEZhbWlseToiY291cmllci1uZXcsIGNvdXJpZXIsIG1vbm9zcGFjZSIsZm9udFNpemU6MTUsZm9udFdlaWdodDoibm9ybWFsIixmb250V2VpZ2h0Qm9sZDoiYm9sZCIsbGluZUhlaWdodDoxLGxpbmtUb29sdGlwSG92ZXJEdXJhdGlvbjo1MDAsbGV0dGVyU3BhY2luZzowLGxvZ0xldmVsOiJpbmZvIixzY3JvbGxiYWNrOjFlMyxzY3JvbGxTZW5zaXRpdml0eToxLHNjcmVlblJlYWRlck1vZGU6ITEsbWFjT3B0aW9uSXNNZXRhOiExLG1hY09wdGlvbkNsaWNrRm9yY2VzU2VsZWN0aW9uOiExLG1pbmltdW1Db250cmFzdFJhdGlvOjEsZGlzYWJsZVN0ZGluOiExLGFsbG93UHJvcG9zZWRBcGk6ITAsYWxsb3dUcmFuc3BhcmVuY3k6ITEsdGFiU3RvcFdpZHRoOjgsdGhlbWU6e30scmlnaHRDbGlja1NlbGVjdHNXb3JkOm8uaXNNYWMscmVuZGVyZXJUeXBlOiJjYW52YXMiLHdpbmRvd09wdGlvbnM6e30sd2luZG93c01vZGU6ITEsd29yZFNlcGFyYXRvcjoiICgpW117fScsXCJgIixhbHRDbGlja01vdmVzQ3Vyc29yOiEwLGNvbnZlcnRFb2w6ITEsdGVybU5hbWU6Inh0ZXJtIixjYW5jZWxFdmVudHM6ITF9O3ZhciBzPVsibm9ybWFsIiwiYm9sZCIsIjEwMCIsIjIwMCIsIjMwMCIsIjQwMCIsIjUwMCIsIjYwMCIsIjcwMCIsIjgwMCIsIjkwMCJdLGE9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe2Zvcih2YXIgciBpbiB0aGlzLl9vbk9wdGlvbkNoYW5nZT1uZXcgbi5FdmVudEVtaXR0ZXIsdGhpcy5fb3B0aW9ucz1pKHt9LHQuREVGQVVMVF9PUFRJT05TKSxlKWlmKHIgaW4gdGhpcy5fb3B0aW9ucyl0cnl7dmFyIG89ZVtyXTt0aGlzLl9vcHRpb25zW3JdPXRoaXMuX3Nhbml0aXplQW5kVmFsaWRhdGVPcHRpb24ocixvKX1jYXRjaChlKXtjb25zb2xlLmVycm9yKGUpfXRoaXMub3B0aW9ucz10aGlzLl9zZXR1cE9wdGlvbnModGhpcy5fb3B0aW9ucyl9cmV0dXJuIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25PcHRpb25DaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25PcHRpb25DaGFuZ2UuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUuX3NldHVwT3B0aW9ucz1mdW5jdGlvbihlKXt2YXIgcj10aGlzLG49aSh7fSxlKSxvPWZ1bmN0aW9uKGUpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShuLGUse2dldDpmdW5jdGlvbigpe2lmKCEoZSBpbiB0LkRFRkFVTFRfT1BUSU9OUykpdGhyb3cgbmV3IEVycm9yKCdObyBvcHRpb24gd2l0aCBrZXkgIicrZSsnIicpO3JldHVybiByLl9vcHRpb25zW2VdfSxzZXQ6ZnVuY3Rpb24oaSl7aWYoIShlIGluIHQuREVGQVVMVF9PUFRJT05TKSl0aHJvdyBuZXcgRXJyb3IoJ05vIG9wdGlvbiB3aXRoIGtleSAiJytlKyciJyk7aT1yLl9zYW5pdGl6ZUFuZFZhbGlkYXRlT3B0aW9uKGUsaSksci5fb3B0aW9uc1tlXSE9PWkmJihyLl9vcHRpb25zW2VdPWksci5fb25PcHRpb25DaGFuZ2UuZmlyZShlKSl9fSl9O2Zvcih2YXIgcyBpbiBuKW8ocyk7cmV0dXJuIG59LGUucHJvdG90eXBlLnNldE9wdGlvbj1mdW5jdGlvbihlLHQpe3RoaXMub3B0aW9uc1tlXT10fSxlLnByb3RvdHlwZS5fc2FuaXRpemVBbmRWYWxpZGF0ZU9wdGlvbj1mdW5jdGlvbihlLHIpe3N3aXRjaChlKXtjYXNlImJlbGxTdHlsZSI6Y2FzZSJjdXJzb3JTdHlsZSI6Y2FzZSJyZW5kZXJlclR5cGUiOmNhc2Uid29yZFNlcGFyYXRvciI6cnx8KHI9dC5ERUZBVUxUX09QVElPTlNbZV0pO2JyZWFrO2Nhc2UiZm9udFdlaWdodCI6Y2FzZSJmb250V2VpZ2h0Qm9sZCI6aWYoIm51bWJlciI9PXR5cGVvZiByJiYxPD1yJiZyPD0xZTMpYnJlYWs7cj1zLmluY2x1ZGVzKHIpP3I6dC5ERUZBVUxUX09QVElPTlNbZV07YnJlYWs7Y2FzZSJjdXJzb3JXaWR0aCI6cj1NYXRoLmZsb29yKHIpO2Nhc2UibGluZUhlaWdodCI6Y2FzZSJ0YWJTdG9wV2lkdGgiOmlmKHI8MSl0aHJvdyBuZXcgRXJyb3IoZSsiIGNhbm5vdCBiZSBsZXNzIHRoYW4gMSwgdmFsdWU6ICIrcik7YnJlYWs7Y2FzZSJtaW5pbXVtQ29udHJhc3RSYXRpbyI6cj1NYXRoLm1heCgxLE1hdGgubWluKDIxLE1hdGgucm91bmQoMTAqcikvMTApKTticmVhaztjYXNlInNjcm9sbGJhY2siOmlmKChyPU1hdGgubWluKHIsNDI5NDk2NzI5NSkpPDApdGhyb3cgbmV3IEVycm9yKGUrIiBjYW5ub3QgYmUgbGVzcyB0aGFuIDAsIHZhbHVlOiAiK3IpO2JyZWFrO2Nhc2UiZmFzdFNjcm9sbFNlbnNpdGl2aXR5IjpjYXNlInNjcm9sbFNlbnNpdGl2aXR5IjppZihyPD0wKXRocm93IG5ldyBFcnJvcihlKyIgY2Fubm90IGJlIGxlc3MgdGhhbiBvciBlcXVhbCB0byAwLCB2YWx1ZTogIityKTtjYXNlInJvd3MiOmNhc2UiY29scyI6aWYoIXImJjAhPT1yKXRocm93IG5ldyBFcnJvcihlKyIgbXVzdCBiZSBudW1lcmljLCB2YWx1ZTogIityKX1yZXR1cm4gcn0sZS5wcm90b3R5cGUuZ2V0T3B0aW9uPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLm9wdGlvbnNbZV19LGV9KCk7dC5PcHRpb25zU2VydmljZT1hfSw4MzQzOihlLHQpPT57ZnVuY3Rpb24gcihlLHQscil7dC5kaSR0YXJnZXQ9PT10P3QuZGkkZGVwZW5kZW5jaWVzLnB1c2goe2lkOmUsaW5kZXg6cn0pOih0LmRpJGRlcGVuZGVuY2llcz1be2lkOmUsaW5kZXg6cn1dLHQuZGkkdGFyZ2V0PXQpfU9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LmNyZWF0ZURlY29yYXRvcj10LmdldFNlcnZpY2VEZXBlbmRlbmNpZXM9dC5zZXJ2aWNlUmVnaXN0cnk9dm9pZCAwLHQuc2VydmljZVJlZ2lzdHJ5PW5ldyBNYXAsdC5nZXRTZXJ2aWNlRGVwZW5kZW5jaWVzPWZ1bmN0aW9uKGUpe3JldHVybiBlLmRpJGRlcGVuZGVuY2llc3x8W119LHQuY3JlYXRlRGVjb3JhdG9yPWZ1bmN0aW9uKGUpe2lmKHQuc2VydmljZVJlZ2lzdHJ5LmhhcyhlKSlyZXR1cm4gdC5zZXJ2aWNlUmVnaXN0cnkuZ2V0KGUpO3ZhciBpPWZ1bmN0aW9uKGUsdCxuKXtpZigzIT09YXJndW1lbnRzLmxlbmd0aCl0aHJvdyBuZXcgRXJyb3IoIkBJU2VydmljZU5hbWUtZGVjb3JhdG9yIGNhbiBvbmx5IGJlIHVzZWQgdG8gZGVjb3JhdGUgYSBwYXJhbWV0ZXIiKTtyKGksZSxuKX07cmV0dXJuIGkudG9TdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm4gZX0sdC5zZXJ2aWNlUmVnaXN0cnkuc2V0KGUsaSksaX19LDI1ODU6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LklVbmljb2RlU2VydmljZT10LklPcHRpb25zU2VydmljZT10LklMb2dTZXJ2aWNlPXQuTG9nTGV2ZWxFbnVtPXQuSUluc3RhbnRpYXRpb25TZXJ2aWNlPXQuSURpcnR5Um93U2VydmljZT10LklDaGFyc2V0U2VydmljZT10LklDb3JlU2VydmljZT10LklDb3JlTW91c2VTZXJ2aWNlPXQuSUJ1ZmZlclNlcnZpY2U9dm9pZCAwO3ZhciBpLG49cig4MzQzKTt0LklCdWZmZXJTZXJ2aWNlPSgwLG4uY3JlYXRlRGVjb3JhdG9yKSgiQnVmZmVyU2VydmljZSIpLHQuSUNvcmVNb3VzZVNlcnZpY2U9KDAsbi5jcmVhdGVEZWNvcmF0b3IpKCJDb3JlTW91c2VTZXJ2aWNlIiksdC5JQ29yZVNlcnZpY2U9KDAsbi5jcmVhdGVEZWNvcmF0b3IpKCJDb3JlU2VydmljZSIpLHQuSUNoYXJzZXRTZXJ2aWNlPSgwLG4uY3JlYXRlRGVjb3JhdG9yKSgiQ2hhcnNldFNlcnZpY2UiKSx0LklEaXJ0eVJvd1NlcnZpY2U9KDAsbi5jcmVhdGVEZWNvcmF0b3IpKCJEaXJ0eVJvd1NlcnZpY2UiKSx0LklJbnN0YW50aWF0aW9uU2VydmljZT0oMCxuLmNyZWF0ZURlY29yYXRvcikoIkluc3RhbnRpYXRpb25TZXJ2aWNlIiksKGk9dC5Mb2dMZXZlbEVudW18fCh0LkxvZ0xldmVsRW51bT17fSkpW2kuREVCVUc9MF09IkRFQlVHIixpW2kuSU5GTz0xXT0iSU5GTyIsaVtpLldBUk49Ml09IldBUk4iLGlbaS5FUlJPUj0zXT0iRVJST1IiLGlbaS5PRkY9NF09Ik9GRiIsdC5JTG9nU2VydmljZT0oMCxuLmNyZWF0ZURlY29yYXRvcikoIkxvZ1NlcnZpY2UiKSx0LklPcHRpb25zU2VydmljZT0oMCxuLmNyZWF0ZURlY29yYXRvcikoIk9wdGlvbnNTZXJ2aWNlIiksdC5JVW5pY29kZVNlcnZpY2U9KDAsbi5jcmVhdGVEZWNvcmF0b3IpKCJVbmljb2RlU2VydmljZSIpfSwxNDgwOihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Vbmljb2RlU2VydmljZT12b2lkIDA7dmFyIGk9cig4NDYwKSxuPXIoMjI1KSxvPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX3Byb3ZpZGVycz1PYmplY3QuY3JlYXRlKG51bGwpLHRoaXMuX2FjdGl2ZT0iIix0aGlzLl9vbkNoYW5nZT1uZXcgaS5FdmVudEVtaXR0ZXI7dmFyIGU9bmV3IG4uVW5pY29kZVY2O3RoaXMucmVnaXN0ZXIoZSksdGhpcy5fYWN0aXZlPWUudmVyc2lvbix0aGlzLl9hY3RpdmVQcm92aWRlcj1lfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uQ2hhbmdlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uQ2hhbmdlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwidmVyc2lvbnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gT2JqZWN0LmtleXModGhpcy5fcHJvdmlkZXJzKX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImFjdGl2ZVZlcnNpb24iLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYWN0aXZlfSxzZXQ6ZnVuY3Rpb24oZSl7aWYoIXRoaXMuX3Byb3ZpZGVyc1tlXSl0aHJvdyBuZXcgRXJyb3IoJ3Vua25vd24gVW5pY29kZSB2ZXJzaW9uICInK2UrJyInKTt0aGlzLl9hY3RpdmU9ZSx0aGlzLl9hY3RpdmVQcm92aWRlcj10aGlzLl9wcm92aWRlcnNbZV0sdGhpcy5fb25DaGFuZ2UuZmlyZShlKX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5yZWdpc3Rlcj1mdW5jdGlvbihlKXt0aGlzLl9wcm92aWRlcnNbZS52ZXJzaW9uXT1lfSxlLnByb3RvdHlwZS53Y3dpZHRoPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9hY3RpdmVQcm92aWRlci53Y3dpZHRoKGUpfSxlLnByb3RvdHlwZS5nZXRTdHJpbmdDZWxsV2lkdGg9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PTAscj1lLmxlbmd0aCxpPTA7aTxyOysraSl7dmFyIG49ZS5jaGFyQ29kZUF0KGkpO2lmKDU1Mjk2PD1uJiZuPD01NjMxOSl7aWYoKytpPj1yKXJldHVybiB0K3RoaXMud2N3aWR0aChuKTt2YXIgbz1lLmNoYXJDb2RlQXQoaSk7NTYzMjA8PW8mJm88PTU3MzQzP249MTAyNCoobi01NTI5Nikrby01NjMyMCs2NTUzNjp0Kz10aGlzLndjd2lkdGgobyl9dCs9dGhpcy53Y3dpZHRoKG4pfXJldHVybiB0fSxlfSgpO3QuVW5pY29kZVNlcnZpY2U9b319LHQ9e307ZnVuY3Rpb24gcihpKXt2YXIgbj10W2ldO2lmKHZvaWQgMCE9PW4pcmV0dXJuIG4uZXhwb3J0czt2YXIgbz10W2ldPXtleHBvcnRzOnt9fTtyZXR1cm4gZVtpXS5jYWxsKG8uZXhwb3J0cyxvLG8uZXhwb3J0cyxyKSxvLmV4cG9ydHN9dmFyIGk9e307cmV0dXJuKCgpPT57dmFyIGU9aTtPYmplY3QuZGVmaW5lUHJvcGVydHkoZSwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksZS5UZXJtaW5hbD12b2lkIDA7dmFyIHQ9cigzMjM2KSxuPXIoOTA0Miksbz1yKDc5NzUpLHM9cig3MDkwKSxhPXIoNTc0MSksYz1yKDgyODUpLGw9WyJjb2xzIiwicm93cyJdLHU9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe3ZhciByPXRoaXM7dGhpcy5fY29yZT1uZXcgdC5UZXJtaW5hbChlKSx0aGlzLl9hZGRvbk1hbmFnZXI9bmV3IGEuQWRkb25NYW5hZ2VyLHRoaXMuX3B1YmxpY09wdGlvbnM9e307dmFyIGk9ZnVuY3Rpb24oZSl7T2JqZWN0LmRlZmluZVByb3BlcnR5KG4uX3B1YmxpY09wdGlvbnMsZSx7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHIuX2NvcmUub3B0aW9uc1tlXX0sc2V0OmZ1bmN0aW9uKHQpe3IuX2NoZWNrUmVhZG9ubHlPcHRpb25zKGUpLHIuX2NvcmUub3B0aW9uc1tlXT10fX0pfSxuPXRoaXM7Zm9yKHZhciBvIGluIHRoaXMuX2NvcmUub3B0aW9ucylpKG8pfXJldHVybiBlLnByb3RvdHlwZS5fY2hlY2tSZWFkb25seU9wdGlvbnM9ZnVuY3Rpb24oZSl7aWYobC5pbmNsdWRlcyhlKSl0aHJvdyBuZXcgRXJyb3IoJ09wdGlvbiAiJytlKyciIGNhbiBvbmx5IGJlIHNldCBpbiB0aGUgY29uc3RydWN0b3InKX0sZS5wcm90b3R5cGUuX2NoZWNrUHJvcG9zZWRBcGk9ZnVuY3Rpb24oKXtpZighdGhpcy5fY29yZS5vcHRpb25zU2VydmljZS5vcHRpb25zLmFsbG93UHJvcG9zZWRBcGkpdGhyb3cgbmV3IEVycm9yKCJZb3UgbXVzdCBzZXQgdGhlIGFsbG93UHJvcG9zZWRBcGkgb3B0aW9uIHRvIHRydWUgdG8gdXNlIHByb3Bvc2VkIEFQSSIpfSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uQmVsbCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uQmVsbH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uQmluYXJ5Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUub25CaW5hcnl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvbkN1cnNvck1vdmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS5vbkN1cnNvck1vdmV9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvbkRhdGEiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS5vbkRhdGF9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvbktleSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uS2V5fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25MaW5lRmVlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uTGluZUZlZWR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvblJlbmRlciIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uUmVuZGVyfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25SZXNpemUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS5vblJlc2l6ZX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uU2Nyb2xsIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUub25TY3JvbGx9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvblNlbGVjdGlvbkNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uU2VsZWN0aW9uQ2hhbmdlfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25UaXRsZUNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uVGl0bGVDaGFuZ2V9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJlbGVtZW50Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUuZWxlbWVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsInBhcnNlciIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jaGVja1Byb3Bvc2VkQXBpKCksdGhpcy5fcGFyc2VyfHwodGhpcy5fcGFyc2VyPW5ldyBvLlBhcnNlckFwaSh0aGlzLl9jb3JlKSksdGhpcy5fcGFyc2VyfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwidW5pY29kZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jaGVja1Byb3Bvc2VkQXBpKCksbmV3IHMuVW5pY29kZUFwaSh0aGlzLl9jb3JlKX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsInRleHRhcmVhIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUudGV4dGFyZWF9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJyb3dzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUucm93c30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImNvbHMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS5jb2xzfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiYnVmZmVyIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NoZWNrUHJvcG9zZWRBcGkoKSx0aGlzLl9idWZmZXJ8fCh0aGlzLl9idWZmZXI9bmV3IGMuQnVmZmVyTmFtZXNwYWNlQXBpKHRoaXMuX2NvcmUpKSx0aGlzLl9idWZmZXJ9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJtYXJrZXJzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NoZWNrUHJvcG9zZWRBcGkoKSx0aGlzLl9jb3JlLm1hcmtlcnN9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJtb2RlcyIse2dldDpmdW5jdGlvbigpe3ZhciBlPXRoaXMuX2NvcmUuY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLHQ9Im5vbmUiO3N3aXRjaCh0aGlzLl9jb3JlLmNvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2wpe2Nhc2UiWDEwIjp0PSJ4MTAiO2JyZWFrO2Nhc2UiVlQyMDAiOnQ9InZ0MjAwIjticmVhaztjYXNlIkRSQUciOnQ9ImRyYWciO2JyZWFrO2Nhc2UiQU5ZIjp0PSJhbnkifXJldHVybnthcHBsaWNhdGlvbkN1cnNvcktleXNNb2RlOmUuYXBwbGljYXRpb25DdXJzb3JLZXlzLGFwcGxpY2F0aW9uS2V5cGFkTW9kZTplLmFwcGxpY2F0aW9uS2V5cGFkLGJyYWNrZXRlZFBhc3RlTW9kZTplLmJyYWNrZXRlZFBhc3RlTW9kZSxpbnNlcnRNb2RlOnRoaXMuX2NvcmUuY29yZVNlcnZpY2UubW9kZXMuaW5zZXJ0TW9kZSxtb3VzZVRyYWNraW5nTW9kZTp0LG9yaWdpbk1vZGU6ZS5vcmlnaW4scmV2ZXJzZVdyYXBhcm91bmRNb2RlOmUucmV2ZXJzZVdyYXBhcm91bmQsc2VuZEZvY3VzTW9kZTplLnNlbmRGb2N1cyx3cmFwYXJvdW5kTW9kZTplLndyYXBhcm91bmR9fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib3B0aW9ucyIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9wdWJsaWNPcHRpb25zfSxzZXQ6ZnVuY3Rpb24oZSl7Zm9yKHZhciB0IGluIGUpdGhpcy5fcHVibGljT3B0aW9uc1t0XT1lW3RdfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmJsdXI9ZnVuY3Rpb24oKXt0aGlzLl9jb3JlLmJsdXIoKX0sZS5wcm90b3R5cGUuZm9jdXM9ZnVuY3Rpb24oKXt0aGlzLl9jb3JlLmZvY3VzKCl9LGUucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuX3ZlcmlmeUludGVnZXJzKGUsdCksdGhpcy5fY29yZS5yZXNpemUoZSx0KX0sZS5wcm90b3R5cGUub3Blbj1mdW5jdGlvbihlKXt0aGlzLl9jb3JlLm9wZW4oZSl9LGUucHJvdG90eXBlLmF0dGFjaEN1c3RvbUtleUV2ZW50SGFuZGxlcj1mdW5jdGlvbihlKXt0aGlzLl9jb3JlLmF0dGFjaEN1c3RvbUtleUV2ZW50SGFuZGxlcihlKX0sZS5wcm90b3R5cGUucmVnaXN0ZXJMaW5rTWF0Y2hlcj1mdW5jdGlvbihlLHQscil7cmV0dXJuIHRoaXMuX2NoZWNrUHJvcG9zZWRBcGkoKSx0aGlzLl9jb3JlLnJlZ2lzdGVyTGlua01hdGNoZXIoZSx0LHIpfSxlLnByb3RvdHlwZS5kZXJlZ2lzdGVyTGlua01hdGNoZXI9ZnVuY3Rpb24oZSl7dGhpcy5fY2hlY2tQcm9wb3NlZEFwaSgpLHRoaXMuX2NvcmUuZGVyZWdpc3RlckxpbmtNYXRjaGVyKGUpfSxlLnByb3RvdHlwZS5yZWdpc3RlckxpbmtQcm92aWRlcj1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fY2hlY2tQcm9wb3NlZEFwaSgpLHRoaXMuX2NvcmUucmVnaXN0ZXJMaW5rUHJvdmlkZXIoZSl9LGUucHJvdG90eXBlLnJlZ2lzdGVyQ2hhcmFjdGVySm9pbmVyPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9jaGVja1Byb3Bvc2VkQXBpKCksdGhpcy5fY29yZS5yZWdpc3RlckNoYXJhY3RlckpvaW5lcihlKX0sZS5wcm90b3R5cGUuZGVyZWdpc3RlckNoYXJhY3RlckpvaW5lcj1mdW5jdGlvbihlKXt0aGlzLl9jaGVja1Byb3Bvc2VkQXBpKCksdGhpcy5fY29yZS5kZXJlZ2lzdGVyQ2hhcmFjdGVySm9pbmVyKGUpfSxlLnByb3RvdHlwZS5yZWdpc3Rlck1hcmtlcj1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fY2hlY2tQcm9wb3NlZEFwaSgpLHRoaXMuX3ZlcmlmeUludGVnZXJzKGUpLHRoaXMuX2NvcmUuYWRkTWFya2VyKGUpfSxlLnByb3RvdHlwZS5hZGRNYXJrZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMucmVnaXN0ZXJNYXJrZXIoZSl9LGUucHJvdG90eXBlLmhhc1NlbGVjdGlvbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLmhhc1NlbGVjdGlvbigpfSxlLnByb3RvdHlwZS5zZWxlY3Q9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX3ZlcmlmeUludGVnZXJzKGUsdCxyKSx0aGlzLl9jb3JlLnNlbGVjdChlLHQscil9LGUucHJvdG90eXBlLmdldFNlbGVjdGlvbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLmdldFNlbGVjdGlvbigpfSxlLnByb3RvdHlwZS5nZXRTZWxlY3Rpb25Qb3NpdGlvbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLmdldFNlbGVjdGlvblBvc2l0aW9uKCl9LGUucHJvdG90eXBlLmNsZWFyU2VsZWN0aW9uPWZ1bmN0aW9uKCl7dGhpcy5fY29yZS5jbGVhclNlbGVjdGlvbigpfSxlLnByb3RvdHlwZS5zZWxlY3RBbGw9ZnVuY3Rpb24oKXt0aGlzLl9jb3JlLnNlbGVjdEFsbCgpfSxlLnByb3RvdHlwZS5zZWxlY3RMaW5lcz1mdW5jdGlvbihlLHQpe3RoaXMuX3ZlcmlmeUludGVnZXJzKGUsdCksdGhpcy5fY29yZS5zZWxlY3RMaW5lcyhlLHQpfSxlLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5fYWRkb25NYW5hZ2VyLmRpc3Bvc2UoKSx0aGlzLl9jb3JlLmRpc3Bvc2UoKX0sZS5wcm90b3R5cGUuc2Nyb2xsTGluZXM9ZnVuY3Rpb24oZSl7dGhpcy5fdmVyaWZ5SW50ZWdlcnMoZSksdGhpcy5fY29yZS5zY3JvbGxMaW5lcyhlKX0sZS5wcm90b3R5cGUuc2Nyb2xsUGFnZXM9ZnVuY3Rpb24oZSl7dGhpcy5fdmVyaWZ5SW50ZWdlcnMoZSksdGhpcy5fY29yZS5zY3JvbGxQYWdlcyhlKX0sZS5wcm90b3R5cGUuc2Nyb2xsVG9Ub3A9ZnVuY3Rpb24oKXt0aGlzLl9jb3JlLnNjcm9sbFRvVG9wKCl9LGUucHJvdG90eXBlLnNjcm9sbFRvQm90dG9tPWZ1bmN0aW9uKCl7dGhpcy5fY29yZS5zY3JvbGxUb0JvdHRvbSgpfSxlLnByb3RvdHlwZS5zY3JvbGxUb0xpbmU9ZnVuY3Rpb24oZSl7dGhpcy5fdmVyaWZ5SW50ZWdlcnMoZSksdGhpcy5fY29yZS5zY3JvbGxUb0xpbmUoZSl9LGUucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7dGhpcy5fY29yZS5jbGVhcigpfSxlLnByb3RvdHlwZS53cml0ZT1mdW5jdGlvbihlLHQpe3RoaXMuX2NvcmUud3JpdGUoZSx0KX0sZS5wcm90b3R5cGUud3JpdGVVdGY4PWZ1bmN0aW9uKGUsdCl7dGhpcy5fY29yZS53cml0ZShlLHQpfSxlLnByb3RvdHlwZS53cml0ZWxuPWZ1bmN0aW9uKGUsdCl7dGhpcy5fY29yZS53cml0ZShlKSx0aGlzLl9jb3JlLndyaXRlKCJcclxuIix0KX0sZS5wcm90b3R5cGUucGFzdGU9ZnVuY3Rpb24oZSl7dGhpcy5fY29yZS5wYXN0ZShlKX0sZS5wcm90b3R5cGUuZ2V0T3B0aW9uPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9jb3JlLm9wdGlvbnNTZXJ2aWNlLmdldE9wdGlvbihlKX0sZS5wcm90b3R5cGUuc2V0T3B0aW9uPWZ1bmN0aW9uKGUsdCl7dGhpcy5fY2hlY2tSZWFkb25seU9wdGlvbnMoZSksdGhpcy5fY29yZS5vcHRpb25zU2VydmljZS5zZXRPcHRpb24oZSx0KX0sZS5wcm90b3R5cGUucmVmcmVzaD1mdW5jdGlvbihlLHQpe3RoaXMuX3ZlcmlmeUludGVnZXJzKGUsdCksdGhpcy5fY29yZS5yZWZyZXNoKGUsdCl9LGUucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5fY29yZS5yZXNldCgpfSxlLnByb3RvdHlwZS5jbGVhclRleHR1cmVBdGxhcz1mdW5jdGlvbigpe3RoaXMuX2NvcmUuY2xlYXJUZXh0dXJlQXRsYXMoKX0sZS5wcm90b3R5cGUubG9hZEFkZG9uPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9hZGRvbk1hbmFnZXIubG9hZEFkZG9uKHRoaXMsZSl9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLCJzdHJpbmdzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIG59LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUuX3ZlcmlmeUludGVnZXJzPWZ1bmN0aW9uKCl7Zm9yKHZhciBlPVtdLHQ9MDt0PGFyZ3VtZW50cy5sZW5ndGg7dCsrKWVbdF09YXJndW1lbnRzW3RdO2Zvcih2YXIgcj0wLGk9ZTtyPGkubGVuZ3RoO3IrKyl7dmFyIG49aVtyXTtpZihuPT09MS8wfHxpc05hTihuKXx8biUxIT0wKXRocm93IG5ldyBFcnJvcigiVGhpcyBBUEkgb25seSBhY2NlcHRzIGludGVnZXJzIil9fSxlfSgpO2UuVGVybWluYWw9dX0pKCksaX0pKCl9fSx0PXt9O2Z1bmN0aW9uIHIoaSl7dmFyIG49dFtpXTtpZih2b2lkIDAhPT1uKXJldHVybiBuLmV4cG9ydHM7dmFyIG89dFtpXT17aWQ6aSxsb2FkZWQ6ITEsZXhwb3J0czp7fX07cmV0dXJuIGVbaV0uY2FsbChvLmV4cG9ydHMsbyxvLmV4cG9ydHMsciksby5sb2FkZWQ9ITAsby5leHBvcnRzfXIubj1lPT57dmFyIHQ9ZSYmZS5fX2VzTW9kdWxlPygpPT5lLmRlZmF1bHQ6KCk9PmU7cmV0dXJuIHIuZCh0LHthOnR9KSx0fSxyLmQ9KGUsdCk9Pntmb3IodmFyIGkgaW4gdClyLm8odCxpKSYmIXIubyhlLGkpJiZPYmplY3QuZGVmaW5lUHJvcGVydHkoZSxpLHtlbnVtZXJhYmxlOiEwLGdldDp0W2ldfSl9LHIuZz1mdW5jdGlvbigpe2lmKCJvYmplY3QiPT10eXBlb2YgZ2xvYmFsVGhpcylyZXR1cm4gZ2xvYmFsVGhpczt0cnl7cmV0dXJuIHRoaXN8fG5ldyBGdW5jdGlvbigicmV0dXJuIHRoaXMiKSgpfWNhdGNoKGUpe2lmKCJvYmplY3QiPT10eXBlb2Ygd2luZG93KXJldHVybiB3aW5kb3d9fSgpLHIubz0oZSx0KT0+T2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGUsdCksci5ubWQ9ZT0+KGUucGF0aHM9W10sZS5jaGlsZHJlbnx8KGUuY2hpbGRyZW49W10pLGUpLCgoKT0+eyJ1c2Ugc3RyaWN0Ijt2YXIgZT1yKDM3OSksdD1yLm4oZSksaT1yKDc5NSksbj1yLm4oaSksbz1yKDU2OSkscz1yLm4obyksYT1yKDU2NSksYz1yLm4oYSksbD1yKDIxNiksdT1yLm4obCksaD1yKDU4OSksZj1yLm4oaCksXz1yKDEwMiksZD17fTtkLnN0eWxlVGFnVHJhbnNmb3JtPWYoKSxkLnNldEF0dHJpYnV0ZXM9YygpLGQuaW5zZXJ0PXMoKS5iaW5kKG51bGwsImhlYWQiKSxkLmRvbUFQST1uKCksZC5pbnNlcnRTdHlsZUVsZW1lbnQ9dSgpLHQoKShfLlosZCksXy5aJiZfLloubG9jYWxzJiZfLloubG9jYWxzO3ZhciBwPXIoMzIwKSx2PXIoNjE3KSxnPXIoNDg2KSx5PXIubihnKSxtPWZ1bmN0aW9uKGUsdCxyLGkpe3JldHVybiBuZXcocnx8KHI9UHJvbWlzZSkpKChmdW5jdGlvbihuLG8pe2Z1bmN0aW9uIHMoZSl7dHJ5e2MoaS5uZXh0KGUpKX1jYXRjaChlKXtvKGUpfX1mdW5jdGlvbiBhKGUpe3RyeXtjKGkudGhyb3coZSkpfWNhdGNoKGUpe28oZSl9fWZ1bmN0aW9uIGMoZSl7dmFyIHQ7ZS5kb25lP24oZS52YWx1ZSk6KHQ9ZS52YWx1ZSx0IGluc3RhbmNlb2Ygcj90Om5ldyByKChmdW5jdGlvbihlKXtlKHQpfSkpKS50aGVuKHMsYSl9YygoaT1pLmFwcGx5KGUsdHx8W10pKS5uZXh0KCkpfSkpfSxiPWZ1bmN0aW9uKGUsdCl7dmFyIHIsaSxuLG8scz17bGFiZWw6MCxzZW50OmZ1bmN0aW9uKCl7aWYoMSZuWzBdKXRocm93IG5bMV07cmV0dXJuIG5bMV19LHRyeXM6W10sb3BzOltdfTtyZXR1cm4gbz17bmV4dDphKDApLHRocm93OmEoMSkscmV0dXJuOmEoMil9LCJmdW5jdGlvbiI9PXR5cGVvZiBTeW1ib2wmJihvW1N5bWJvbC5pdGVyYXRvcl09ZnVuY3Rpb24oKXtyZXR1cm4gdGhpc30pLG87ZnVuY3Rpb24gYShvKXtyZXR1cm4gZnVuY3Rpb24oYSl7cmV0dXJuIGZ1bmN0aW9uKG8pe2lmKHIpdGhyb3cgbmV3IFR5cGVFcnJvcigiR2VuZXJhdG9yIGlzIGFscmVhZHkgZXhlY3V0aW5nLiIpO2Zvcig7czspdHJ5e2lmKHI9MSxpJiYobj0yJm9bMF0/aS5yZXR1cm46b1swXT9pLnRocm93fHwoKG49aS5yZXR1cm4pJiZuLmNhbGwoaSksMCk6aS5uZXh0KSYmIShuPW4uY2FsbChpLG9bMV0pKS5kb25lKXJldHVybiBuO3N3aXRjaChpPTAsbiYmKG89WzImb1swXSxuLnZhbHVlXSksb1swXSl7Y2FzZSAwOmNhc2UgMTpuPW87YnJlYWs7Y2FzZSA0OnJldHVybiBzLmxhYmVsKysse3ZhbHVlOm9bMV0sZG9uZTohMX07Y2FzZSA1OnMubGFiZWwrKyxpPW9bMV0sbz1bMF07Y29udGludWU7Y2FzZSA3Om89cy5vcHMucG9wKCkscy50cnlzLnBvcCgpO2NvbnRpbnVlO2RlZmF1bHQ6aWYoISgobj0obj1zLnRyeXMpLmxlbmd0aD4wJiZuW24ubGVuZ3RoLTFdKXx8NiE9PW9bMF0mJjIhPT1vWzBdKSl7cz0wO2NvbnRpbnVlfWlmKDM9PT1vWzBdJiYoIW58fG9bMV0+blswXSYmb1sxXTxuWzNdKSl7cy5sYWJlbD1vWzFdO2JyZWFrfWlmKDY9PT1vWzBdJiZzLmxhYmVsPG5bMV0pe3MubGFiZWw9blsxXSxuPW87YnJlYWt9aWYobiYmcy5sYWJlbDxuWzJdKXtzLmxhYmVsPW5bMl0scy5vcHMucHVzaChvKTticmVha31uWzJdJiZzLm9wcy5wb3AoKSxzLnRyeXMucG9wKCk7Y29udGludWV9bz10LmNhbGwoZSxzKX1jYXRjaChlKXtvPVs2LGVdLGk9MH1maW5hbGx5e3I9bj0wfWlmKDUmb1swXSl0aHJvdyBvWzFdO3JldHVybnt2YWx1ZTpvWzBdP29bMV06dm9pZCAwLGRvbmU6ITB9fShbbyxhXSl9fX07d2luZG93Lm9ubG9hZD1mdW5jdGlvbigpe3ZhciBlPW5ldyBwLlRlcm1pbmFsLHQ9bmV3IHYuRml0QWRkb247d2luZG93LnRlcm09ZSx3aW5kb3cuZml0QWRkb249dCxlLmxvYWRBZGRvbih0KSxlLm9wZW4oZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInRlcm1pbmFsIikpO3ZhciByPWZ1bmN0aW9uKCl7ZS5lbGVtZW50LnBhcmVudEVsZW1lbnQuc3R5bGUuaGVpZ2h0PXdpbmRvdy5pbm5lckhlaWdodC0xNisicHgiLHQuZml0KCksZmV0Y2goIi9yZXNpemU/cm93cz0iK2Uucm93cysiJmNvbHM9IitlLmNvbHMpfTtyKCksd2luZG93Lm9ucmVzaXplPXI7dmFyIGk9W107ZS5vbkRhdGEoKGZ1bmN0aW9uKGUpe2kucHVzaChlKX0pKSxtKHRoaXMsdm9pZCAwLHZvaWQgMCwoZnVuY3Rpb24oKXt2YXIgZSx0LHI7cmV0dXJuIGIodGhpcywoZnVuY3Rpb24obil7c3dpdGNoKG4ubGFiZWwpe2Nhc2UgMDplPWZ1bmN0aW9uKGUpe3JldHVybiBuZXcgUHJvbWlzZSgoZnVuY3Rpb24odCl7cmV0dXJuIHNldFRpbWVvdXQodCxlKX0pKX0sbi5sYWJlbD0xO2Nhc2UgMTpuLnRyeXMucHVzaChbMSwsNyw4XSksbi5sYWJlbD0yO2Nhc2UgMjpyZXR1cm5bNCxlKDEwMCldO2Nhc2UgMzpyZXR1cm4gbi5zZW50KCkseSgpLmlzRW1wdHkoaSk/WzMsNV06KHQ9aS5qb2luKCIiKSxyPXdpbmRvdy5idG9hKHQpLGkubGVuZ3RoPTAsWzQsZmV0Y2goIi9pbi8iK3IpXSk7Y2FzZSA0Om4uc2VudCgpLG4ubGFiZWw9NTtjYXNlIDU6cmV0dXJuWzMsMl07Y2FzZSA2OnJldHVyblszLDhdO2Nhc2UgNzpyZXR1cm4gY29uc29sZS5sb2coImlucHV0IGRpc2Nvbm5lY3QhIiksWzddO2Nhc2UgODpyZXR1cm5bMl19fSkpfSkpLGZ1bmN0aW9uKCl7bSh0aGlzLHZvaWQgMCx2b2lkIDAsKGZ1bmN0aW9uKCl7dmFyIHQscixpO3JldHVybiBiKHRoaXMsKGZ1bmN0aW9uKG4pe3N3aXRjaChuLmxhYmVsKXtjYXNlIDA6bi50cnlzLnB1c2goWzAsLDUsNl0pLG4ubGFiZWw9MTtjYXNlIDE6cmV0dXJuWzQsZmV0Y2goIi9vdXQiKV07Y2FzZSAyOnJldHVybiB0PW4uc2VudCgpLGk9VWludDhBcnJheS5iaW5kLFs0LHQuYXJyYXlCdWZmZXIoKV07Y2FzZSAzOnJldHVybiByPW5ldyhpLmFwcGx5KFVpbnQ4QXJyYXksW3ZvaWQgMCxuLnNlbnQoKV0pKSx0JiZlLndyaXRlKHIpLFszLDFdO2Nhc2UgNDpyZXR1cm5bMyw2XTtjYXNlIDU6cmV0dXJuIGNvbnNvbGUubG9nKCJpbnB1dCBkaXNjb25uZWN0ISIpLFs3XTtjYXNlIDY6cmV0dXJuWzJdfX0pKX0pKX0oKX19KSgpfSkoKTs=", + "ok": true, + "headers": [ + [ + "content-length", + "426644" + ], + [ + "content-type", + "text/javascript" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/out": { + "data": "W3N1cGVyZ2F0ZXdheV0gUE9TVCAvbWVzc2FnZSAtPiBTU0UgdHJhbnNwb3J0DQpbc3VwZXJnYXRld2F5XSBTU0UgLT4gQ2hpbGQ6IHsianNvbnJwYyI6IjIuMCIsImlkIjowLCJtZXRob2QiOiJpbml0aWFsaXplIiwicGFyYW1zIjp7InByb3RvY29sVmVyc2lvbiI6IjIwMjQtMTEtMDUiLCJjYXBhYmlsaXRpZXMiOnsicm9vdHMiOnsibGlzdENoYW5nZWQiOnRydWV9fSwiY2xpZW50SW5mbyI6eyJuYW1lIjoibWNwIiwidmVyc2lvbiI6IjAuMS4wIn19fQ0KW3N1cGVyZ2F0ZXdheV0gQ2hpbGQgLT4gU1NFOiB7DQogIHJlc3VsdDogew0KICAgIHByb3RvY29sVmVyc2lvbjogG1szMm0nMjAyNC0xMS0wNScbWzM5bSwNCiAgICBjYXBhYmlsaXRpZXM6IHsgdG9vbHM6IHt9IH0sDQogICAgc2VydmVySW5mbzogeyBuYW1lOiAbWzMybSdzZWN1cmUtZmlsZXN5c3RlbS1zZXJ2ZXInG1szOW0sIHZlcnNpb246IBtbMzJtJzAuMi4wJxtbMzltIH0NCiAgfSwNCiAganNvbnJwYzogG1szMm0nMi4wJxtbMzltLA0KICBpZDogG1szM20wG1szOW0NCn0NCltzdXBlcmdhdGV3YXldIFBPU1QgL21lc3NhZ2UgLT4gU1NFIHRyYW5zcG9ydA0KW3N1cGVyZ2F0ZXdheV0gU1NFIC0+IENoaWxkOiB7Impzb25ycGMiOiIyLjAiLCJtZXRob2QiOiJub3RpZmljYXRpb25zL2luaXRpYWxpemVkIn0NCltzdXBlcmdhdGV3YXldIFBPU1QgL21lc3NhZ2UgLT4gU1NFIHRyYW5zcG9ydA0KW3N1cGVyZ2F0ZXdheV0gU1NFIC0+IENoaWxkOiB7Impzb25ycGMiOiIyLjAiLCJpZCI6MSwibWV0aG9kIjoidG9vbHMvY2FsbCIsInBhcmFtcyI6eyJuYW1lIjoibGlzdF9kaXJlY3RvcnkiLCJhcmd1bWVudHMiOnsic2Vzc2lvbl9pZCI6IjI1ZmU0OWQwLTg4YzAtNGQ3OC05MDFhLWI3YmQyMTBhNGQ1MiIsInBhdGgiOiIvY29udGVudCJ9fX0NCltzdXBlcmdhdGV3YXldIENoaWxkIC0+IFNTRTogeyByZXN1bHQ6IHsgY29udGVudDogWyAbWzM2bVtPYmplY3RdG1szOW0gXSB9LCBqc29ucnBjOiAbWzMybScyLjAnG1szOW0sIGlkOiAbWzMzbTEbWzM5bSB9DQpbc3VwZXJnYXRld2F5XSBTU0UgY29ubmVjdGlvbiBjbG9zZWQuDQo=", + "ok": true, + "headers": [ + [ + "content-length", + "1067" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/resize?rows=46&cols=196": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/G1syMDB+bnB4IC15IHN1cGVyZ2F0ZXdheSAtLXBvcnQgODAwMCAtLXN0ZGlvICducHggLXkgQG1vZGVsY29udGV4dHByb3RvY29sL3NlcnZlci1maWxlc3lzdGVtIC9jb250ZW50JxtbMjAxfg==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/DQ==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Aw==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/DA==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/dA==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/b3U=": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Yw==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/aCA=": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Zg==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/bw==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/bw0=": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/dQ==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Y2g=": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/IA==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Yg==": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/YXI=": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/G1tB": { + "data": "", + "ok": true, + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "status": 200, + "status_text": "" + } + }, + "base_uri": "https://localhost:8080/", + "height": 839 + }, + "id": "giIA2M-ANUIM", + "outputId": "612c3487-1fd7-41ab-f65a-690b1325f46d" + }, + "id": "giIA2M-ANUIM", + "execution_count": 9, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "Launching Xterm..." + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "\n", + " (async () => {\n", + " const url = new URL(await google.colab.kernel.proxyPort(10000, {'cache': true}));\n", + " const iframe = document.createElement('iframe');\n", + " iframe.src = url;\n", + " iframe.setAttribute('width', '100%');\n", + " iframe.setAttribute('height', '800');\n", + " iframe.setAttribute('frameborder', 0);\n", + " document.body.appendChild(iframe);\n", + " })();\n", + " " + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Register the toolgroup hosted in the MCP server with llama stack and verify if the stack discovers the tools correctly" + ], + "metadata": { + "id": "f4ksBP6MN7cB" + }, + "id": "f4ksBP6MN7cB" + }, + { + "cell_type": "code", + "source": [ + "from llama_stack_client.types.shared_params.url import URL\n", + "client.toolgroups.register(\n", + " toolgroup_id=\"mcp::filesystem\",\n", + " provider_id=\"model-context-protocol\",\n", + " mcp_endpoint=URL(uri=\"http://localhost:8000/sse\"),\n", + ")" + ], + "metadata": { + "id": "DwdKhQb1N295" + }, + "id": "DwdKhQb1N295", + "execution_count": 10, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "pprint(client.tools.list(toolgroup_id=\"mcp::filesystem\"))" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "ZZ5_vIkDOyAN", + "outputId": "f6fa8639-c2d8-497d-f4ed-716b3bf775d4" + }, + "id": "ZZ5_vIkDOyAN", + "execution_count": 11, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Read the complete contents of a file from the file system. Handles various text encodings and provides detailed error messages if the file cannot be read. Use this tool when you need to examine the contents of a single file. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'read_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'read_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Read\u001b[0m\u001b[32m the contents of multiple files simultaneously. This is more efficient than reading files one by one when you need to analyze or compare multiple files. Each file's content is returned with its path as a reference. Failed reads for individual files won't stop the entire operation. Only works within allowed directories.\"\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'read_multiple_files'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'paths'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'read_multiple_files'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Create a new file or completely overwrite an existing file with new content. Use with caution as it will overwrite existing files without warning. Handles text content with proper encoding. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'write_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'content'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'write_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Make line-based edits to a text file. Each edit replaces exact line sequences with new content. Returns a git-style diff showing the changes made. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'edit_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'edits'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Preview changes using git-style diff format'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'dryRun'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mparameter_type\u001b[0m=\u001b[32m'boolean'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'edit_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Create a new directory or ensure a directory exists. Can create multiple nested directories in one operation. If the directory already exists, this operation will succeed silently. Perfect for setting up directory structures for projects or ensuring required paths exist. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'create_directory'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'create_directory'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Get a detailed listing of all files and directories in a specified path. Results clearly distinguish between files and directories with \u001b[0m\u001b[32m[\u001b[0m\u001b[32mFILE\u001b[0m\u001b[32m]\u001b[0m\u001b[32m and \u001b[0m\u001b[32m[\u001b[0m\u001b[32mDIR\u001b[0m\u001b[32m]\u001b[0m\u001b[32m prefixes. This tool is essential for understanding directory structure and finding specific files within a directory. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'list_directory'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'list_directory'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Get\u001b[0m\u001b[32m a recursive tree view of files and directories as a JSON structure. Each entry includes 'name', 'type' \u001b[0m\u001b[32m(\u001b[0m\u001b[32mfile/directory\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, and 'children' for directories. Files have no children array, while directories always have a children array \u001b[0m\u001b[32m(\u001b[0m\u001b[32mwhich may be empty\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. The output is formatted with 2-space indentation for readability. Only works within allowed directories.\"\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'directory_tree'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'directory_tree'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Move or rename files and directories. Can move files between directories and rename them in a single operation. If the destination exists, the operation will fail. Works across different directories and can be used for simple renaming within the same directory. Both source and destination must be within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'move_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'source'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'destination'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'move_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Recursively\u001b[0m\u001b[32m search for files and directories matching a pattern. Searches through all subdirectories from the starting path. The search is case-insensitive and matches partial names. Returns full paths to all matching items. Great for finding files when you don't know their exact location. Only searches within allowed directories.\"\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'search_files'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'pattern'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'excludePatterns'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'search_files'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Retrieve detailed metadata about a file or directory. Returns comprehensive information including size, creation time, last modified time, permissions, and type. This tool is perfect for understanding file characteristics without reading the actual content. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'get_file_info'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'get_file_info'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Returns the list of directories that this server is allowed to access. Use this to understand which directories are available before trying to access files.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'list_allowed_directories'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'list_allowed_directories'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[1m]\u001b[0m\n" + ], + "text/html": [ + "
[\n",
+              "Tool(\n",
+              "│   │   description='Read the complete contents of a file from the file system. Handles various text encodings and provides detailed error messages if the file cannot be read. Use this tool when you need to examine the contents of a single file. Only works within allowed directories.',\n",
+              "│   │   identifier='read_file',\n",
+              "│   │   parameters=[Parameter(description='', name='path', parameter_type='string', required=True, default=None)],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='read_file',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description=\"Read the contents of multiple files simultaneously. This is more efficient than reading files one by one when you need to analyze or compare multiple files. Each file's content is returned with its path as a reference. Failed reads for individual files won't stop the entire operation. Only works within allowed directories.\",\n",
+              "│   │   identifier='read_multiple_files',\n",
+              "│   │   parameters=[Parameter(description='', name='paths', parameter_type='array', required=True, default=None)],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='read_multiple_files',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description='Create a new file or completely overwrite an existing file with new content. Use with caution as it will overwrite existing files without warning. Handles text content with proper encoding. Only works within allowed directories.',\n",
+              "│   │   identifier='write_file',\n",
+              "│   │   parameters=[\n",
+              "│   │   │   Parameter(description='', name='path', parameter_type='string', required=True, default=None),\n",
+              "│   │   │   Parameter(description='', name='content', parameter_type='string', required=True, default=None)\n",
+              "│   │   ],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='write_file',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description='Make line-based edits to a text file. Each edit replaces exact line sequences with new content. Returns a git-style diff showing the changes made. Only works within allowed directories.',\n",
+              "│   │   identifier='edit_file',\n",
+              "│   │   parameters=[\n",
+              "│   │   │   Parameter(description='', name='path', parameter_type='string', required=True, default=None),\n",
+              "│   │   │   Parameter(description='', name='edits', parameter_type='array', required=True, default=None),\n",
+              "│   │   │   Parameter(\n",
+              "│   │   │   │   description='Preview changes using git-style diff format',\n",
+              "│   │   │   │   name='dryRun',\n",
+              "│   │   │   │   parameter_type='boolean',\n",
+              "│   │   │   │   required=True,\n",
+              "│   │   │   │   default=None\n",
+              "│   │   │   )\n",
+              "│   │   ],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='edit_file',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description='Create a new directory or ensure a directory exists. Can create multiple nested directories in one operation. If the directory already exists, this operation will succeed silently. Perfect for setting up directory structures for projects or ensuring required paths exist. Only works within allowed directories.',\n",
+              "│   │   identifier='create_directory',\n",
+              "│   │   parameters=[Parameter(description='', name='path', parameter_type='string', required=True, default=None)],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='create_directory',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description='Get a detailed listing of all files and directories in a specified path. Results clearly distinguish between files and directories with [FILE] and [DIR] prefixes. This tool is essential for understanding directory structure and finding specific files within a directory. Only works within allowed directories.',\n",
+              "│   │   identifier='list_directory',\n",
+              "│   │   parameters=[Parameter(description='', name='path', parameter_type='string', required=True, default=None)],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='list_directory',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description=\"Get a recursive tree view of files and directories as a JSON structure. Each entry includes 'name', 'type' (file/directory), and 'children' for directories. Files have no children array, while directories always have a children array (which may be empty). The output is formatted with 2-space indentation for readability. Only works within allowed directories.\",\n",
+              "│   │   identifier='directory_tree',\n",
+              "│   │   parameters=[Parameter(description='', name='path', parameter_type='string', required=True, default=None)],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='directory_tree',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description='Move or rename files and directories. Can move files between directories and rename them in a single operation. If the destination exists, the operation will fail. Works across different directories and can be used for simple renaming within the same directory. Both source and destination must be within allowed directories.',\n",
+              "│   │   identifier='move_file',\n",
+              "│   │   parameters=[\n",
+              "│   │   │   Parameter(description='', name='source', parameter_type='string', required=True, default=None),\n",
+              "│   │   │   Parameter(description='', name='destination', parameter_type='string', required=True, default=None)\n",
+              "│   │   ],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='move_file',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description=\"Recursively search for files and directories matching a pattern. Searches through all subdirectories from the starting path. The search is case-insensitive and matches partial names. Returns full paths to all matching items. Great for finding files when you don't know their exact location. Only searches within allowed directories.\",\n",
+              "│   │   identifier='search_files',\n",
+              "│   │   parameters=[\n",
+              "│   │   │   Parameter(description='', name='path', parameter_type='string', required=True, default=None),\n",
+              "│   │   │   Parameter(description='', name='pattern', parameter_type='string', required=True, default=None),\n",
+              "│   │   │   Parameter(\n",
+              "│   │   │   │   description='',\n",
+              "│   │   │   │   name='excludePatterns',\n",
+              "│   │   │   │   parameter_type='array',\n",
+              "│   │   │   │   required=True,\n",
+              "│   │   │   │   default=None\n",
+              "│   │   │   )\n",
+              "│   │   ],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='search_files',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description='Retrieve detailed metadata about a file or directory. Returns comprehensive information including size, creation time, last modified time, permissions, and type. This tool is perfect for understanding file characteristics without reading the actual content. Only works within allowed directories.',\n",
+              "│   │   identifier='get_file_info',\n",
+              "│   │   parameters=[Parameter(description='', name='path', parameter_type='string', required=True, default=None)],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='get_file_info',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              "),\n",
+              "Tool(\n",
+              "│   │   description='Returns the list of directories that this server is allowed to access. Use this to understand which directories are available before trying to access files.',\n",
+              "│   │   identifier='list_allowed_directories',\n",
+              "│   │   parameters=[],\n",
+              "│   │   provider_id='model-context-protocol',\n",
+              "│   │   provider_resource_id='list_allowed_directories',\n",
+              "│   │   tool_host='model_context_protocol',\n",
+              "│   │   toolgroup_id='mcp::filesystem',\n",
+              "│   │   type='tool',\n",
+              "│   │   metadata={'endpoint': 'http://localhost:8000/sse'}\n",
+              ")\n",
+              "]\n",
+              "
\n" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "code", + "source": [ + "from llama_stack_client.lib.agents.agent import Agent\n", + "from llama_stack_client.lib.agents.event_logger import EventLogger\n", + "from llama_stack_client.types.agent_create_params import AgentConfig\n", + "from termcolor import cprint\n", + "\n", + "agent_config = AgentConfig(\n", + " model=model_id,\n", + " instructions=\"You are a helpful assistant\",\n", + " toolgroups=[\"mcp::filesystem\"],\n", + " input_shields=[],\n", + " output_shields=[],\n", + " enable_session_persistence=False,\n", + ")\n", + "agent = Agent(client, agent_config)\n", + "user_prompts = [\n", + " \"Hello\",\n", + " \"list all the files /content\",\n", + "]\n", + "\n", + "session_id = agent.create_session(\"test-session\")\n", + "for prompt in user_prompts:\n", + " cprint(f\"User> {prompt}\", \"green\")\n", + " response = agent.create_turn(\n", + " messages=[\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": prompt,\n", + " }\n", + " ],\n", + " session_id=session_id,\n", + " )\n", + " for log in EventLogger().log(response):\n", + " log.print()\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vttLbj_YO01f", + "outputId": "04bc486c-3a61-49c6-d0d2-4a211d6de0b5" + }, + "id": "vttLbj_YO01f", + "execution_count": 12, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "User> Hello\n", + "inference> None of the provided functions can be used to respond to a greeting.\n", + "User> list all the files /content\n", + "inference> {\"type\": \"function\", \"name\": \"list_directory\", \"parameters\": {\"path\": \"/content\"}}\n", + "tool_execution> Tool:list_directory Args:{'path': '/content'}\n", + "tool_execution> Tool:list_directory Response:{\"type\":\"text\",\"text\":\"[DIR] .config\\n[FILE] bar\\n[FILE] foo\\n[DIR] sample_data\"}\n", + "inference> {\"type\": \"function\", \"name\": \"list_directory\", \"parameters\": {\"path\": \"/content\"}}\n", + "tool_execution> Tool:list_directory Args:{'path': '/content'}\n", + "tool_execution> Tool:list_directory Response:{\"type\":\"text\",\"text\":\"[DIR] .config\\n[FILE] bar\\n[FILE] foo\\n[DIR] sample_data\"}\n", + "inference> The list of files in the /content directory is:\n", + "\n", + "[DIR] .config\n", + "[FILE] bar\n", + "[FILE] foo\n", + "[DIR] sample_data\n" + ] + } + ] + }, { "cell_type": "markdown", "id": "FJ85DUhgBZd7", @@ -2417,7 +3443,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "id": "4iCO59kP20Zs", "metadata": { "colab": { @@ -2504,7 +3530,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "id": "agkWgToGAsuA", "metadata": { "colab": { @@ -2741,7 +3767,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "id": "sy4Xaff_Avuu", "metadata": { "colab": { @@ -2886,7 +3912,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "id": "xG4Y84VQBb0g", "metadata": { "colab": { @@ -3030,37 +4056,6 @@ }, "widgets": { "application/vnd.jupyter.widget-state+json": { - "01b3e7803d1946118d27acda0c067da2": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "02b60dad91c7482ba70cf8bb954bc4eb": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, "02baf670942347d69c290452de8641e4": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -3189,42 +4184,6 @@ "width": null } }, - "0b64892a98d14a3b85b128df77d8e7d6": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_542aa4a847cf4a66a4b3fc93c241363b", - "placeholder": "​", - "style": "IPY_MODEL_8c0d69b735c94b719160d39256c643cc", - "value": " 112/112 [00:00<00:00, 6.51kB/s]" - } - }, - "0c0b30e126724f9282ac5acbcb4581db": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, "0c2e30d78c234b1b8098d879442d3bac": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -3277,112 +4236,6 @@ "width": null } }, - "0f3bbf28fbed4e97b660bbf3c66a214a": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "0f699b0f99484a8ba2eb17bb1d621c5a": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "0fd62e56e0bb41a996c04e63381d2a29": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "1030c0848635497681cc9ff0c344fb1a": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_29badfc2eb0345d38d7cfc6c7f8bb1a8", - "max": 116, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_e64cedb4560a43d8a43f36002087ac30", - "value": 116 - } - }, "10bc8be68b5545fd8609824b02499ebf": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -3435,110 +4288,6 @@ "width": null } }, - "111184729957441d9d1f3d404bd82757": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_be060f9d7a664c17a80510f447c0bee3", - "IPY_MODEL_228445132e5f4b2ca793f4beeeca4426", - "IPY_MODEL_b96a2e34a2af435b9705550fe564591d" - ], - "layout": "IPY_MODEL_1f1cdac013af4559889f15eebac5256a" - } - }, - "1307ef0325bb433d8a1bcc653c7fb291": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "130f2f5840764e8dbd573cc8a6ea6f5f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "1377d2160344430da8f29a50d113a288": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "15ae23892b634a9f821a8fcee14e500b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -3561,58 +4310,6 @@ "layout": "IPY_MODEL_3ded85d9c34246e88f8ce693eb8025e5" } }, - "1756eceba2c34c1ca182b7db465e95ce": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "1817f6732a5f44c7adc75a644b1acef2": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -3629,204 +4326,6 @@ "description_width": "" } }, - "18ed62b1d4594ed9a2651fa5df046efc": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_95db8eab3f964edf99038ad53f41fabc", - "placeholder": "​", - "style": "IPY_MODEL_52f1d69c6cd04816b6f34657893ae32b", - "value": " 10.7k/10.7k [00:00<00:00, 223kB/s]" - } - }, - "1b7af9f7204547b8b4a718a780af0ded": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1c86d856083c4ef99976849c7a1c9100": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_67f82b82ebb74d0fb3c68b9c8c57d690", - "placeholder": "​", - "style": "IPY_MODEL_b710cb57f19d4490a740c060e8a83b90", - "value": " 350/350 [00:00<00:00, 26.0kB/s]" - } - }, - "1f1cdac013af4559889f15eebac5256a": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1f1dc0d20cae46feb372203aea6458a0": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "20a66f9de4ed41c7ac9a8e817898ed9e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -3864,60 +4363,6 @@ "description_width": "" } }, - "228445132e5f4b2ca793f4beeeca4426": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_48a5b775a4324da791603b83d61be7d1", - "max": 612, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_02b60dad91c7482ba70cf8bb954bc4eb", - "value": 612 - } - }, - "24c0be775e474517a7be49d187822bd0": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "2563a4677dde47d0a2f7fba5c5dde358": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, "2574b07e4af24715aa89d048cc84e358": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -3939,27 +4384,6 @@ "value": " 1/1 [00:00<00:00, 15.08it/s]" } }, - "25821e7aef4e481bbdf3b4698ce3c277": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_7daef1502e2a4140ac021b3b3a6aa12d", - "placeholder": "​", - "style": "IPY_MODEL_1307ef0325bb433d8a1bcc653c7fb291", - "value": " 466k/466k [00:00<00:00, 2.16MB/s]" - } - }, "269b1ad9dc7b4ebb94d7364c75f3f324": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4085,58 +4509,6 @@ "value": " 1/1 [00:01<00:00,  1.24s/it]" } }, - "29badfc2eb0345d38d7cfc6c7f8bb1a8": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "2b2046db907349798e3ae774c15b25d2": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4189,110 +4561,6 @@ "width": null } }, - "2bfb0fb5506d4285918a9c94af9ab5d1": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2e27a025a416434f8ab3b63049626d11": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "2eff72cbd9bb4f1ca77213602caa9417": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4315,255 +4583,6 @@ "layout": "IPY_MODEL_10bc8be68b5545fd8609824b02499ebf" } }, - "3015bc3ce98a4221a9dd3be92481435d": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "309ea9620a674088a5207206d9a52d54": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_4d7b0983b97f48b2a333d5b2a4ec50a8", - "max": 350, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_e834a64e49534c3586cb77f4ec5eab2d", - "value": 350 - } - }, - "31ab98e0e375416b83b36a98d4958f57": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_90c2e0e012a94521b9f5cb24924771d8", - "placeholder": "​", - "style": "IPY_MODEL_2563a4677dde47d0a2f7fba5c5dde358", - "value": " 90.9M/90.9M [00:00<00:00, 223MB/s]" - } - }, - "35e10db3906248ffa8ab955d2f53bd75": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1f1dc0d20cae46feb372203aea6458a0", - "placeholder": "​", - "style": "IPY_MODEL_43feace0290a47c0b06c3a1c08cc70a9", - "value": "tokenizer.json: 100%" - } - }, - "366add01dc734455a384460c97491215": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_0f3bbf28fbed4e97b660bbf3c66a214a", - "max": 190, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_a4b2220ed47f4f85b3f991c92de98964", - "value": 190 - } - }, - "38a958036c6e4155815a8169f1be1e53": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3a46a46bc8124a92b27aef43cbc009b6": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3a649adc22694036b35bab04ff03d338": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "3ac596104cdc4439b3980f7ce66ad080": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_40e9f20d74374b0e82c653caa0559d04", - "max": 53, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_f46cfc9237e64db6be2ec6529b61ec88", - "value": 53 - } - }, "3c18f449359f422f950543bd976fe323": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4579,49 +4598,6 @@ "description_width": "" } }, - "3c868641db934c67a44e1d26e1a17756": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_a72d01788b484bbeb4375aac3ceadf34", - "IPY_MODEL_366add01dc734455a384460c97491215", - "IPY_MODEL_70accb92e645435b8f1e0c48538f7473" - ], - "layout": "IPY_MODEL_628848757fcf443e806a8f25013cc2b5" - } - }, - "3da95c8814f34472a181ce7687f9e15e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_53a46fe254924e78876db6dd2e1b7123", - "placeholder": "​", - "style": "IPY_MODEL_f2ce01983f0a4f12b318e6d29f1dd4a1", - "value": "model.safetensors: 100%" - } - }, "3ded85d9c34246e88f8ce693eb8025e5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4726,110 +4702,6 @@ "width": null } }, - "4004cda1d84949f5a380536f8a9d0274": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "40e9f20d74374b0e82c653caa0559d04": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "42335bcbc6ee40a79d36c5159cc7da06": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4897,21 +4769,6 @@ "description_width": "" } }, - "43feace0290a47c0b06c3a1c08cc70a9": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, "44e34588d6854737b0fb14b4b6a62a95": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4933,58 +4790,6 @@ "value": "Batches: 100%" } }, - "45aadb26b382460eb5b6b147509fb75a": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "4709067f3f554b93b3ef35e3f58cbf85": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5023,94 +4828,6 @@ "layout": "IPY_MODEL_e61fdef1dc4b4d809168c0b441b0e6ac" } }, - "487477e023b64947bf42f83dc6275ef1": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_a92a7bce961e4291b126fda3c540636b", - "placeholder": "​", - "style": "IPY_MODEL_01b3e7803d1946118d27acda0c067da2", - "value": " 232k/232k [00:00<00:00, 550kB/s]" - } - }, - "48a5b775a4324da791603b83d61be7d1": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4ad6bc0cca62446d8faf19a341bfa86f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, "4b83e3caa8ec47169dca04ee9599adeb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5135,290 +4852,6 @@ "value": 1 } }, - "4d1c2de4c1354ef0b84c54c447141707": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1b7af9f7204547b8b4a718a780af0ded", - "max": 90868376, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_a4bb5a59d1324585b0a34c9bb2820b7f", - "value": 90868376 - } - }, - "4d7b0983b97f48b2a333d5b2a4ec50a8": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "5023c2b8cf9846069d116237826fed7f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_960c2f44166b4ac7910af6512832186f", - "IPY_MODEL_309ea9620a674088a5207206d9a52d54", - "IPY_MODEL_1c86d856083c4ef99976849c7a1c9100" - ], - "layout": "IPY_MODEL_5d9bf2102da143c1b9e1483e05add4e5" - } - }, - "509863a58de74b07b813aa83ffa4a507": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "52f1d69c6cd04816b6f34657893ae32b": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "53a46fe254924e78876db6dd2e1b7123": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "542aa4a847cf4a66a4b3fc93c241363b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "54bddcf41c5641b7a56c981aadb62ef1": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "5a620017a5384af1a056de687b2670db": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5435,183 +4868,6 @@ "description_width": "" } }, - "5c9ec25994914acd8e13866b3eb943e1": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_dc04575da46540d4ad3a708e58f0de6a", - "placeholder": "​", - "style": "IPY_MODEL_24c0be775e474517a7be49d187822bd0", - "value": " 53.0/53.0 [00:00<00:00, 3.84kB/s]" - } - }, - "5d9bf2102da143c1b9e1483e05add4e5": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "5effefa8e3764e3aaff57fe0197a7c96": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "628848757fcf443e806a8f25013cc2b5": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "631c9a95127244c79875c829a7637df6": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -5664,110 +4920,6 @@ "width": null } }, - "6437c99289f947449f7d2964288973e5": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "67f82b82ebb74d0fb3c68b9c8c57d690": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "69e5263c812c4542a9e5c31fefaa37fe": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5783,70 +4935,6 @@ "description_width": "" } }, - "70accb92e645435b8f1e0c48538f7473": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_b6a505e6c863409db1b906423f99125a", - "placeholder": "​", - "style": "IPY_MODEL_d9560d20106a42ec904e7e315f99ff01", - "value": " 190/190 [00:00<00:00, 9.18kB/s]" - } - }, - "713c09d1275a43b0af7c2ae8e126517f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_b62fe08114f549ea99808e8df95c7cad", - "IPY_MODEL_af722d177320422e97c679b24cb754f6", - "IPY_MODEL_487477e023b64947bf42f83dc6275ef1" - ], - "layout": "IPY_MODEL_bcf0d3af3bc0439e97023937852941e9" - } - }, - "7363b1a9a1b54a57bf15357e897128fd": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_cf5113a647ce45c4a3a523361aa3b5af", - "placeholder": "​", - "style": "IPY_MODEL_da8c20a65ba541bda058614849d5cfe2", - "value": "sentence_bert_config.json: 100%" - } - }, "7551b282ef3a4387a801637de2d5c76e": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -5914,21 +5002,6 @@ "description_width": "" } }, - "79b9fb75dc1d486c9fc881a90b6f1060": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, "7cc356ed20e94401b72a0e138ad0f5df": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5951,82 +5024,6 @@ "layout": "IPY_MODEL_e662ba10fbae49d9b66172125dfc0717" } }, - "7daef1502e2a4140ac021b3b3a6aa12d": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "80e884cae6ea42eaa37f028120963355": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_9f185162847f4cb2828af81c92116582", - "max": 466247, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_3a649adc22694036b35bab04ff03d338", - "value": 466247 - } - }, "811f115733b14ab4b242a8b11526016c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -6048,273 +5045,6 @@ "value": " 1/1 [00:00<00:00, 13.00it/s]" } }, - "834ae2d249b94be6bbe5349509536a4b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "85569eaf3ae3488b808131cd460f6514": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "88f0c88612bb45d59f07e93567cc0e14": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_9b24a82117e1482a8f6665978e84089c", - "IPY_MODEL_8e75bf7cac454eeabd5ce47a1e981c68", - "IPY_MODEL_fc272883566541108f83117ccd146a21" - ], - "layout": "IPY_MODEL_2e27a025a416434f8ab3b63049626d11" - } - }, - "895efd0b6d9f4b319159703d965d1966": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_dece6dff65394a5f93585c73359d4dad", - "IPY_MODEL_1030c0848635497681cc9ff0c344fb1a", - "IPY_MODEL_fa6ecaab432347de8427b9b5ac3d4524" - ], - "layout": "IPY_MODEL_5effefa8e3764e3aaff57fe0197a7c96" - } - }, - "8ab411217bfd486ca3fb8b885fff4690": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "8b9ebe06b4e045a29269128ec97d9f62": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "8c0d69b735c94b719160d39256c643cc": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, "8d370762fafd4d7887ff68ea8279d083": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -6367,58 +5097,6 @@ "width": null } }, - "8de1cba3a7c0422eb2a21e3f8b2059c7": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "8dee873065a047799a04e49ab791e449": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -6443,259 +5121,6 @@ "value": 1 } }, - "8e75bf7cac454eeabd5ce47a1e981c68": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_6437c99289f947449f7d2964288973e5", - "max": 349, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_e2f7dea8fc744537b42d0f1a85a73eb4", - "value": 349 - } - }, - "90c2e0e012a94521b9f5cb24924771d8": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "916190b4615e4c5c9f3e55c0804a3502": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "91e103573c034ceda689047c61294b17": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "95db8eab3f964edf99038ad53f41fabc": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "960c2f44166b4ac7910af6512832186f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_85569eaf3ae3488b808131cd460f6514", - "placeholder": "​", - "style": "IPY_MODEL_3015bc3ce98a4221a9dd3be92481435d", - "value": "tokenizer_config.json: 100%" - } - }, "980292182c7144e194604c13ac544a26": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -6717,42 +5142,6 @@ "value": "Batches: 100%" } }, - "9b11eaf2d50a447384b75eb7f73829eb": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "9b24a82117e1482a8f6665978e84089c": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_3a46a46bc8124a92b27aef43cbc009b6", - "placeholder": "​", - "style": "IPY_MODEL_4ad6bc0cca62446d8faf19a341bfa86f", - "value": "modules.json: 100%" - } - }, "9bb8bf12010f42b2b17c10c7ccaa7bf8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -6821,252 +5210,6 @@ "width": null } }, - "9ee45247ec144bb3aafe4208f316063f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_da330e0999cb4c3c91a1cb1026304568", - "IPY_MODEL_ff58a5381fb74cb1b9efc10f5c2738d6", - "IPY_MODEL_18ed62b1d4594ed9a2651fa5df046efc" - ], - "layout": "IPY_MODEL_4004cda1d84949f5a380536f8a9d0274" - } - }, - "9f185162847f4cb2828af81c92116582": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a0639d5360044f97ac5b9374c735ff4b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a4b2220ed47f4f85b3f991c92de98964": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "a4bb5a59d1324585b0a34c9bb2820b7f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "a72d01788b484bbeb4375aac3ceadf34": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_ebf411690c844daf89b87c120e3cb67e", - "placeholder": "​", - "style": "IPY_MODEL_79b9fb75dc1d486c9fc881a90b6f1060", - "value": "1_Pooling/config.json: 100%" - } - }, - "a92a7bce961e4291b126fda3c540636b": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a9a0d8415d9d4e98a3f02ae8ec1053da": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, "acd39276db17439798a97abc56460b0f": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -7088,30 +5231,6 @@ "value": "Batches: 100%" } }, - "af722d177320422e97c679b24cb754f6": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_91e103573c034ceda689047c61294b17", - "max": 231508, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_b9eac61fb55342f4bf9834f321899836", - "value": 231508 - } - }, "b28d46c2ecdd46b9b3f2da871afbf1cb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -7133,27 +5252,6 @@ "value": "Batches: 100%" } }, - "b62fe08114f549ea99808e8df95c7cad": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_d83a1e1e678e4efd83115f9aee0ffc8d", - "placeholder": "​", - "style": "IPY_MODEL_f210583576594e759387fc704695ad09", - "value": "vocab.txt: 100%" - } - }, "b6a0eb553b024a71b737ff47ca8f7633": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -7169,208 +5267,6 @@ "description_width": "" } }, - "b6a505e6c863409db1b906423f99125a": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b710cb57f19d4490a740c060e8a83b90": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "b79a1dfcf2904bcba332569dbf351f34": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_7363b1a9a1b54a57bf15357e897128fd", - "IPY_MODEL_3ac596104cdc4439b3980f7ce66ad080", - "IPY_MODEL_5c9ec25994914acd8e13866b3eb943e1" - ], - "layout": "IPY_MODEL_38a958036c6e4155815a8169f1be1e53" - } - }, - "b7f9a3c97f2043f380bdc1827961c649": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_8ab411217bfd486ca3fb8b885fff4690", - "max": 112, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_c80ea8c54211427087712b5500e26edf", - "value": 112 - } - }, - "b96a2e34a2af435b9705550fe564591d": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_2bfb0fb5506d4285918a9c94af9ab5d1", - "placeholder": "​", - "style": "IPY_MODEL_0f699b0f99484a8ba2eb17bb1d621c5a", - "value": " 612/612 [00:00<00:00, 47.5kB/s]" - } - }, - "b9eac61fb55342f4bf9834f321899836": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "bcf0d3af3bc0439e97023937852941e9": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "bda474c3b8184597a6a9bc6da0672a50": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -7395,65 +5291,6 @@ "value": 1 } }, - "be060f9d7a664c17a80510f447c0bee3": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_834ae2d249b94be6bbe5349509536a4b", - "placeholder": "​", - "style": "IPY_MODEL_509863a58de74b07b813aa83ffa4a507", - "value": "config.json: 100%" - } - }, - "c6f34317390e4f90b16235f2ae84a981": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_3da95c8814f34472a181ce7687f9e15e", - "IPY_MODEL_4d1c2de4c1354ef0b84c54c447141707", - "IPY_MODEL_31ab98e0e375416b83b36a98d4958f57" - ], - "layout": "IPY_MODEL_8b9ebe06b4e045a29269128ec97d9f62" - } - }, - "c80ea8c54211427087712b5500e26edf": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, "c83c23161674484e81f0db9856c23eb6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -7475,58 +5312,6 @@ "value": " 1/1 [00:00<00:00, 14.00it/s]" } }, - "cceff1126242494bab432205c7ac7345": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "cf453a1ed54645aba656f9a3f1461e69": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -7542,58 +5327,6 @@ "description_width": "" } }, - "cf5113a647ce45c4a3a523361aa3b5af": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "cf694e1b797246b096ae588973dc985f": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -7795,198 +5528,6 @@ "width": null } }, - "d83a1e1e678e4efd83115f9aee0ffc8d": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "d9560d20106a42ec904e7e315f99ff01": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "da330e0999cb4c3c91a1cb1026304568": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_54bddcf41c5641b7a56c981aadb62ef1", - "placeholder": "​", - "style": "IPY_MODEL_a9a0d8415d9d4e98a3f02ae8ec1053da", - "value": "README.md: 100%" - } - }, - "da8c20a65ba541bda058614849d5cfe2": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "dc04575da46540d4ad3a708e58f0de6a": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "dece6dff65394a5f93585c73359d4dad": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1756eceba2c34c1ca182b7db465e95ce", - "placeholder": "​", - "style": "IPY_MODEL_0fd62e56e0bb41a996c04e63381d2a29", - "value": "config_sentence_transformers.json: 100%" - } - }, - "e2f7dea8fc744537b42d0f1a85a73eb4": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, "e61fdef1dc4b4d809168c0b441b0e6ac": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -8039,22 +5580,6 @@ "width": null } }, - "e64cedb4560a43d8a43f36002087ac30": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, "e662ba10fbae49d9b66172125dfc0717": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -8107,22 +5632,6 @@ "width": null } }, - "e6e53c439dab4639adc1c3c873602476": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, "e82b5196209f4b9f919c7abb402a4504": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -8144,74 +5653,6 @@ "value": "Batches: 100%" } }, - "e834a64e49534c3586cb77f4ec5eab2d": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "ebf411690c844daf89b87c120e3cb67e": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "ec747bd7c37c45298896c513634cd59a": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -8301,159 +5742,6 @@ "layout": "IPY_MODEL_3ec694106303491ea112a257309bc69c" } }, - "f01d7a1404a943a08c84adce14a262c7": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_f15cdedf8e7b4a44993644a5ff070e78", - "IPY_MODEL_b7f9a3c97f2043f380bdc1827961c649", - "IPY_MODEL_0b64892a98d14a3b85b128df77d8e7d6" - ], - "layout": "IPY_MODEL_8de1cba3a7c0422eb2a21e3f8b2059c7" - } - }, - "f097b32928f246de9b01fea6f9b092f7": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_35e10db3906248ffa8ab955d2f53bd75", - "IPY_MODEL_80e884cae6ea42eaa37f028120963355", - "IPY_MODEL_25821e7aef4e481bbdf3b4698ce3c277" - ], - "layout": "IPY_MODEL_916190b4615e4c5c9f3e55c0804a3502" - } - }, - "f15cdedf8e7b4a44993644a5ff070e78": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_a0639d5360044f97ac5b9374c735ff4b", - "placeholder": "​", - "style": "IPY_MODEL_9b11eaf2d50a447384b75eb7f73829eb", - "value": "special_tokens_map.json: 100%" - } - }, - "f210583576594e759387fc704695ad09": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "f2ce01983f0a4f12b318e6d29f1dd4a1": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "f46cfc9237e64db6be2ec6529b61ec88": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "fa6ecaab432347de8427b9b5ac3d4524": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_45aadb26b382460eb5b6b147509fb75a", - "placeholder": "​", - "style": "IPY_MODEL_130f2f5840764e8dbd573cc8a6ea6f5f", - "value": " 116/116 [00:00<00:00, 3.35kB/s]" - } - }, - "fc272883566541108f83117ccd146a21": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1377d2160344430da8f29a50d113a288", - "placeholder": "​", - "style": "IPY_MODEL_0c0b30e126724f9282ac5acbcb4581db", - "value": " 349/349 [00:00<00:00, 7.72kB/s]" - } - }, "fe34706489c14253a5015ff6332ec4e0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -8478,10 +5766,53 @@ "value": 1 } }, - "ff58a5381fb74cb1b9efc10f5c2738d6": { + "75307e3dee604d30aa44713e6e293e64": { "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_5ce87402a79342af995df41ac3940d55", + "IPY_MODEL_fbbcc19886cc43b38424fbb184162c61", + "IPY_MODEL_29212208db6b432eb4f708cd64258954" + ], + "layout": "IPY_MODEL_50dd8994a4cf486ebbec5ffd4322992a" + } + }, + "5ce87402a79342af995df41ac3940d55": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f9b768c703494dd198f2978aff4892e8", + "placeholder": "​", + "style": "IPY_MODEL_1231b9e4cab34c33a38bee63543f1e75", + "value": "modules.json: 100%" + } + }, + "fbbcc19886cc43b38424fbb184162c61": { + "model_module": "@jupyter-widgets/controls", "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -8494,13 +5825,3708 @@ "bar_style": "success", "description": "", "description_tooltip": null, - "layout": "IPY_MODEL_cceff1126242494bab432205c7ac7345", + "layout": "IPY_MODEL_754deb3970604d48a522bc9f021ad945", + "max": 349, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_f6ecca7a1a8340fbbe056235a2714fc3", + "value": 349 + } + }, + "29212208db6b432eb4f708cd64258954": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ef4f63fe9d8f4683a9d20becb6e4e2cb", + "placeholder": "​", + "style": "IPY_MODEL_7508f10c13634e7aa682cfb29c48d9e7", + "value": " 349/349 [00:00<00:00, 19.2kB/s]" + } + }, + "50dd8994a4cf486ebbec5ffd4322992a": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f9b768c703494dd198f2978aff4892e8": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "1231b9e4cab34c33a38bee63543f1e75": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "754deb3970604d48a522bc9f021ad945": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "f6ecca7a1a8340fbbe056235a2714fc3": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "ef4f63fe9d8f4683a9d20becb6e4e2cb": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7508f10c13634e7aa682cfb29c48d9e7": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "26f1430ca7cb4ad5b1b8df1ffdbd32a9": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_7cd2d9c9ea7b4d70902ffaff33033078", + "IPY_MODEL_101288236cff40b8bb9dbad80dbbc7ee", + "IPY_MODEL_d5c9977838a249eeab6ef628279b8155" + ], + "layout": "IPY_MODEL_d032d1e7b4b54ba28ac83c1a12b23876" + } + }, + "7cd2d9c9ea7b4d70902ffaff33033078": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_321fce57c158432abeae496ae8a947aa", + "placeholder": "​", + "style": "IPY_MODEL_3ebe00201bdb4e119e3b74f684a58345", + "value": "config_sentence_transformers.json: 100%" + } + }, + "101288236cff40b8bb9dbad80dbbc7ee": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0f8bab6b8ed04774b386fe952aae66f1", + "max": 116, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_cfcb6e456c354d99be91f161552f3376", + "value": 116 + } + }, + "d5c9977838a249eeab6ef628279b8155": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_61bd0d490c0e4c04a331cf9ce6b7d38f", + "placeholder": "​", + "style": "IPY_MODEL_7d8653fca29f4df3a7487733ff9db60b", + "value": " 116/116 [00:00<00:00, 5.06kB/s]" + } + }, + "d032d1e7b4b54ba28ac83c1a12b23876": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "321fce57c158432abeae496ae8a947aa": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3ebe00201bdb4e119e3b74f684a58345": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "0f8bab6b8ed04774b386fe952aae66f1": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "cfcb6e456c354d99be91f161552f3376": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "61bd0d490c0e4c04a331cf9ce6b7d38f": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7d8653fca29f4df3a7487733ff9db60b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "943f8fcb66614353a51f32f8344b6122": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_0e695245b97c4bbc85e349fda3dc07b9", + "IPY_MODEL_bb0d168c41f540b8ae42239d3938483a", + "IPY_MODEL_87700a80125348f28c4f249bdf8b0a8d" + ], + "layout": "IPY_MODEL_8902c3622da540e496ed5b1524bd01ca" + } + }, + "0e695245b97c4bbc85e349fda3dc07b9": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_90432ec1c24b4607a935c94e130cd68d", + "placeholder": "​", + "style": "IPY_MODEL_464147b149824f20afc727751a702fc7", + "value": "README.md: 100%" + } + }, + "bb0d168c41f540b8ae42239d3938483a": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_67e37a088be64a2ba786ca923b1017dd", "max": 10659, "min": 0, "orientation": "horizontal", - "style": "IPY_MODEL_e6e53c439dab4639adc1c3c873602476", + "style": "IPY_MODEL_98786f52ef5345b0b9164b9c1f2b8e18", "value": 10659 } + }, + "87700a80125348f28c4f249bdf8b0a8d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0e1b9910a77d4b7fa69cb8926e6547d7", + "placeholder": "​", + "style": "IPY_MODEL_0b276315be4345be83da1e03905c8495", + "value": " 10.7k/10.7k [00:00<00:00, 862kB/s]" + } + }, + "8902c3622da540e496ed5b1524bd01ca": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "90432ec1c24b4607a935c94e130cd68d": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "464147b149824f20afc727751a702fc7": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "67e37a088be64a2ba786ca923b1017dd": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "98786f52ef5345b0b9164b9c1f2b8e18": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "0e1b9910a77d4b7fa69cb8926e6547d7": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0b276315be4345be83da1e03905c8495": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "e11f8c3891284e07bd2572257afd5e1b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_ee18d96394994d01b49d5b03b3d9a019", + "IPY_MODEL_844b06df5749441fab6f61656ce581a9", + "IPY_MODEL_e1c6b9a20e074f17aeba976b24e80c65" + ], + "layout": "IPY_MODEL_c690da8daa1e4f9ea73bcacdd92e8a6d" + } + }, + "ee18d96394994d01b49d5b03b3d9a019": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d0b161ae25c441e8b3caf7a3d88c1b05", + "placeholder": "​", + "style": "IPY_MODEL_47cf4b6b835d43388576a2abf4cc54f8", + "value": "sentence_bert_config.json: 100%" + } + }, + "844b06df5749441fab6f61656ce581a9": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_03bbebd659e64b5d9c29a73570c34854", + "max": 53, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_b68e5097d2504d2cbd7e19aa1aac3a04", + "value": 53 + } + }, + "e1c6b9a20e074f17aeba976b24e80c65": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_22a665deff88477b9372c0350c4c572b", + "placeholder": "​", + "style": "IPY_MODEL_5e535ed2b83e496ab57b1c80b615ab0c", + "value": " 53.0/53.0 [00:00<00:00, 4.23kB/s]" + } + }, + "c690da8daa1e4f9ea73bcacdd92e8a6d": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d0b161ae25c441e8b3caf7a3d88c1b05": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "47cf4b6b835d43388576a2abf4cc54f8": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "03bbebd659e64b5d9c29a73570c34854": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b68e5097d2504d2cbd7e19aa1aac3a04": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "22a665deff88477b9372c0350c4c572b": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5e535ed2b83e496ab57b1c80b615ab0c": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "d9de065c7f81443e98ddf066c7b5bd54": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_1e836106837c4ac7a11b36e700c46b64", + "IPY_MODEL_55591e8179084fcfa3a61c8bd8d09dcb", + "IPY_MODEL_de1ef93c41364eda9b4b111231057348" + ], + "layout": "IPY_MODEL_23b0b2f4f82c4a21846e91d7cea91da5" + } + }, + "1e836106837c4ac7a11b36e700c46b64": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_9e4d0fbb51284a7487c495c7b95a293d", + "placeholder": "​", + "style": "IPY_MODEL_b0f8cf1f79e04b5fb47a810f2c81bd7e", + "value": "config.json: 100%" + } + }, + "55591e8179084fcfa3a61c8bd8d09dcb": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0c359bc4c94c46acbc9094354a15c33d", + "max": 612, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_59d0b59b6c2248508d0601ff13878d33", + "value": 612 + } + }, + "de1ef93c41364eda9b4b111231057348": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_891cb726d45c4fef8f2c74a56df5532b", + "placeholder": "​", + "style": "IPY_MODEL_fa39189070334939aea5fa4a7de5ec8b", + "value": " 612/612 [00:00<00:00, 48.3kB/s]" + } + }, + "23b0b2f4f82c4a21846e91d7cea91da5": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9e4d0fbb51284a7487c495c7b95a293d": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b0f8cf1f79e04b5fb47a810f2c81bd7e": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "0c359bc4c94c46acbc9094354a15c33d": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "59d0b59b6c2248508d0601ff13878d33": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "891cb726d45c4fef8f2c74a56df5532b": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "fa39189070334939aea5fa4a7de5ec8b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "f0e107dd6d54483aa367da0e337a97cd": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_861a00796f55470e85d94733eeee9a5f", + "IPY_MODEL_5459633eb6e94ec391d13fcf67425726", + "IPY_MODEL_b7b7467ece304ffbbd352b9b96a03aad" + ], + "layout": "IPY_MODEL_9dece059f1204e29b106fca9e191ddb3" + } + }, + "861a00796f55470e85d94733eeee9a5f": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_e2e49c25d6fc4592b317e94cfabc2e5e", + "placeholder": "​", + "style": "IPY_MODEL_76d37a48a73946bab2821f097cf2605f", + "value": "model.safetensors: 100%" + } + }, + "5459633eb6e94ec391d13fcf67425726": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8e81ae00681347cb906b392c3656a64a", + "max": 90868376, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_74bedc38b7da4e8a83b0c892d7aa59b5", + "value": 90868376 + } + }, + "b7b7467ece304ffbbd352b9b96a03aad": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d1e67c28b4664e8098dce8f5e80b8779", + "placeholder": "​", + "style": "IPY_MODEL_abe6cf39b784436993fcbe92221c31a3", + "value": " 90.9M/90.9M [00:00<00:00, 215MB/s]" + } + }, + "9dece059f1204e29b106fca9e191ddb3": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "e2e49c25d6fc4592b317e94cfabc2e5e": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "76d37a48a73946bab2821f097cf2605f": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "8e81ae00681347cb906b392c3656a64a": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "74bedc38b7da4e8a83b0c892d7aa59b5": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "d1e67c28b4664e8098dce8f5e80b8779": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "abe6cf39b784436993fcbe92221c31a3": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "d021a18ab70b4c7e8aec43932a124c36": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_72e7c092fb054b7ea0dcd2782b5d8a7d", + "IPY_MODEL_8b1ea80221174fae943d5c9f997dfb57", + "IPY_MODEL_f8073d625f80415dbf712cee434f6e3a" + ], + "layout": "IPY_MODEL_5f6014ba13fa4a659b9eb1b5f83599a7" + } + }, + "72e7c092fb054b7ea0dcd2782b5d8a7d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_327ff8f5292d47afbfebd3beea187739", + "placeholder": "​", + "style": "IPY_MODEL_988cac4341b646079fc73719f3f88ad7", + "value": "tokenizer_config.json: 100%" + } + }, + "8b1ea80221174fae943d5c9f997dfb57": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_900a4dac08f540dfb35c29f63236a12c", + "max": 350, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_1e6009b9b0684b8fbaa379ea96f111ee", + "value": 350 + } + }, + "f8073d625f80415dbf712cee434f6e3a": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_541b9b4e74614e2cb855bb90f03df538", + "placeholder": "​", + "style": "IPY_MODEL_ff256b2275f740ed82bca4f43b4d6fd2", + "value": " 350/350 [00:00<00:00, 23.3kB/s]" + } + }, + "5f6014ba13fa4a659b9eb1b5f83599a7": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "327ff8f5292d47afbfebd3beea187739": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "988cac4341b646079fc73719f3f88ad7": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "900a4dac08f540dfb35c29f63236a12c": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "1e6009b9b0684b8fbaa379ea96f111ee": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "541b9b4e74614e2cb855bb90f03df538": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ff256b2275f740ed82bca4f43b4d6fd2": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "3703041a499c426bb427ee008c81cde5": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_4b22bbacb995425fb32a2368f3685a92", + "IPY_MODEL_49a66eeb9ef74de5ab8904fd90eb7558", + "IPY_MODEL_08f9d125018b41c582a0fa1e234315f9" + ], + "layout": "IPY_MODEL_736c770230644894b85dbc34bd8f1d52" + } + }, + "4b22bbacb995425fb32a2368f3685a92": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_b67cbbf32f844a19b219be612d5038c9", + "placeholder": "​", + "style": "IPY_MODEL_774b513d64524ac7823a2cf13efa8d41", + "value": "vocab.txt: 100%" + } + }, + "49a66eeb9ef74de5ab8904fd90eb7558": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1e56da93bcf64ff490416d2b66cd3dc0", + "max": 231508, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_b7e35038ce344110b785753b655130f5", + "value": 231508 + } + }, + "08f9d125018b41c582a0fa1e234315f9": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_5472af91737446f4a4a2d92a3f684a45", + "placeholder": "​", + "style": "IPY_MODEL_9fb4368802da4a5a8101ba200d98403a", + "value": " 232k/232k [00:00<00:00, 3.18MB/s]" + } + }, + "736c770230644894b85dbc34bd8f1d52": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b67cbbf32f844a19b219be612d5038c9": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "774b513d64524ac7823a2cf13efa8d41": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "1e56da93bcf64ff490416d2b66cd3dc0": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b7e35038ce344110b785753b655130f5": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "5472af91737446f4a4a2d92a3f684a45": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9fb4368802da4a5a8101ba200d98403a": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "2e713bcc372e48b2a006558db4d1df68": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_1a277abd5ea44253bc6894bef258b52b", + "IPY_MODEL_b3eedd82e7da4ce8b3ded70e49a2afd0", + "IPY_MODEL_6f5c18cb8002471f8b3764effee37324" + ], + "layout": "IPY_MODEL_3bebac362b344e8d9103c5011613f1ea" + } + }, + "1a277abd5ea44253bc6894bef258b52b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_670905a55b19458da69f83c8bcd511d1", + "placeholder": "​", + "style": "IPY_MODEL_ff54451a48394faaaa9d8cdb690d0718", + "value": "tokenizer.json: 100%" + } + }, + "b3eedd82e7da4ce8b3ded70e49a2afd0": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_36b5bc19b2d0407f8ab28ff0da2ce12d", + "max": 466247, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_879e48d9a9e04183903d94ffe98313d2", + "value": 466247 + } + }, + "6f5c18cb8002471f8b3764effee37324": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_abce503d70594c2ca9afdc47847c125b", + "placeholder": "​", + "style": "IPY_MODEL_028e291ee53947bbbbc4bfb68c695f5f", + "value": " 466k/466k [00:00<00:00, 3.52MB/s]" + } + }, + "3bebac362b344e8d9103c5011613f1ea": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "670905a55b19458da69f83c8bcd511d1": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "ff54451a48394faaaa9d8cdb690d0718": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "36b5bc19b2d0407f8ab28ff0da2ce12d": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "879e48d9a9e04183903d94ffe98313d2": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "abce503d70594c2ca9afdc47847c125b": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "028e291ee53947bbbbc4bfb68c695f5f": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "a530662719374c95a9bef12e59e28c85": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_bffc0f4b12f141398535990709fd4f2c", + "IPY_MODEL_04804c74e1dd43449d5f758cf5d0ba5e", + "IPY_MODEL_95a506c3007c4525b01ee4e1600d671b" + ], + "layout": "IPY_MODEL_a0d6b0caeb2340fe96c8f5569e3d3ae4" + } + }, + "bffc0f4b12f141398535990709fd4f2c": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_30798f87a8b848d783fdacd71af5dc04", + "placeholder": "​", + "style": "IPY_MODEL_07ce54c75e76488ba4019a20b3707061", + "value": "special_tokens_map.json: 100%" + } + }, + "04804c74e1dd43449d5f758cf5d0ba5e": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f023175de68445f98a6b01bb40ccdc6d", + "max": 112, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_7389b79a0ff44cd68c7866995d728023", + "value": 112 + } + }, + "95a506c3007c4525b01ee4e1600d671b": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8e2b70ffe4eb4974bd6393fcc1292267", + "placeholder": "​", + "style": "IPY_MODEL_13eee164dc534424acb9dc9ee37a9465", + "value": " 112/112 [00:00<00:00, 8.09kB/s]" + } + }, + "a0d6b0caeb2340fe96c8f5569e3d3ae4": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "30798f87a8b848d783fdacd71af5dc04": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "07ce54c75e76488ba4019a20b3707061": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "f023175de68445f98a6b01bb40ccdc6d": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7389b79a0ff44cd68c7866995d728023": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "8e2b70ffe4eb4974bd6393fcc1292267": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "13eee164dc534424acb9dc9ee37a9465": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "722a7fe16af3422585a20c651345cfa4": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HBoxModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_f5596c1c9c4d42f3bc171961f9582eff", + "IPY_MODEL_85d66e615b5742e78657b1e60c75fc72", + "IPY_MODEL_731c02dc5dd446c3b22765575148e256" + ], + "layout": "IPY_MODEL_254ce460ce244c99a5afe39d5d51f6b7" + } + }, + "f5596c1c9c4d42f3bc171961f9582eff": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_4cf1dc345ace4da59f978f661487f975", + "placeholder": "​", + "style": "IPY_MODEL_8f30fca71bf24e5ca26e17c2321f893c", + "value": "1_Pooling/config.json: 100%" + } + }, + "85d66e615b5742e78657b1e60c75fc72": { + "model_module": "@jupyter-widgets/controls", + "model_name": "FloatProgressModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_dd85d37dd1d14c7ea4592f8e11b2d2c8", + "max": 190, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_3cb06377e4454f009d6b2aa7aa6ff0a9", + "value": 190 + } + }, + "731c02dc5dd446c3b22765575148e256": { + "model_module": "@jupyter-widgets/controls", + "model_name": "HTMLModel", + "model_module_version": "1.5.0", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_4502477db4d948e693012364c2dcb370", + "placeholder": "​", + "style": "IPY_MODEL_52fe404ec9c14db2a7279b4c154eef3d", + "value": " 190/190 [00:00<00:00, 12.8kB/s]" + } + }, + "254ce460ce244c99a5afe39d5d51f6b7": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "4cf1dc345ace4da59f978f661487f975": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8f30fca71bf24e5ca26e17c2321f893c": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "dd85d37dd1d14c7ea4592f8e11b2d2c8": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3cb06377e4454f009d6b2aa7aa6ff0a9": { + "model_module": "@jupyter-widgets/controls", + "model_name": "ProgressStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "4502477db4d948e693012364c2dcb370": { + "model_module": "@jupyter-widgets/base", + "model_name": "LayoutModel", + "model_module_version": "1.2.0", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "52fe404ec9c14db2a7279b4c154eef3d": { + "model_module": "@jupyter-widgets/controls", + "model_name": "DescriptionStyleModel", + "model_module_version": "1.5.0", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } } } } From e41873f26852e291aac1b9ab4f0dfd70302a5280 Mon Sep 17 00:00:00 2001 From: Sixian Yi Date: Tue, 21 Jan 2025 21:10:24 -0800 Subject: [PATCH 066/107] [ez] structured output for /completion ollama & enable tests (#822) # What does this PR do? 1) enabled structured output for ollama /completion API. It seems we missed this one. 2) fixed ollama structured output test in client sdk - ollama does not support list format for structured output 3) enable structured output unit test as the result was stable on Llama-3.1-8B-Instruct and ollama, fireworks, together. ## Test Plan 1) Run `test_completion_structured_output` on /completion API with 3 providers: ollama, fireworks, together. pytest -v -s -k "together" --inference-model="meta-llama/Llama-3.1-8B-Instruct" llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_completion_structured_output ``` (base) sxyi@sxyi-mbp llama-stack % pytest -s -v llama_stack/providers/tests/inference --config=ci_test_config.yaml /Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/pytest_asyncio/plugin.py:208: PytestDeprecationWarning: The configuration option "asyncio_default_fixture_loop_scope" is unset. The event loop scope for asynchronous fixtures will default to the fixture caching scope. Future versions of pytest-asyncio will default the loop scope for asynchronous fixtures to function scope. Set the default fixture loop scope explicitly in order to avoid unexpected behavior in the future. Valid fixture loop scopes are: "function", "class", "module", "package", "session" warnings.warn(PytestDeprecationWarning(_DEFAULT_FIXTURE_LOOP_SCOPE_UNSET)) ================================================================================================ test session starts ================================================================================================= platform darwin -- Python 3.13.0, pytest-8.3.4, pluggy-1.5.0 -- /Library/Frameworks/Python.framework/Versions/3.13/bin/python3.13 cachedir: .pytest_cache metadata: {'Python': '3.13.0', 'Platform': 'macOS-15.1.1-arm64-arm-64bit-Mach-O', 'Packages': {'pytest': '8.3.4', 'pluggy': '1.5.0'}, 'Plugins': {'asyncio': '0.24.0', 'html': '4.1.1', 'metadata': '3.1.1', 'md': '0.2.0', 'dependency': '0.6.0', 'md-report': '0.6.3', 'anyio': '4.6.2.post1'}} rootdir: /Users/sxyi/llama-stack configfile: pyproject.toml plugins: asyncio-0.24.0, html-4.1.1, metadata-3.1.1, md-0.2.0, dependency-0.6.0, md-report-0.6.3, anyio-4.6.2.post1 asyncio: mode=Mode.STRICT, default_loop_scope=None collected 85 items / 82 deselected / 3 selected llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_completion_structured_output[meta-llama/Llama-3.1-8B-Instruct-ollama] PASSED llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_completion_structured_output[meta-llama/Llama-3.1-8B-Instruct-fireworks] PASSED llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_completion_structured_output[meta-llama/Llama-3.1-8B-Instruct-together] PASSED ==================================================================================== 3 passed, 82 deselected, 8 warnings in 5.67s ==================================================================================== ``` 2) ` LLAMA_STACK_CONFIG="./llama_stack/templates/ollama/run.yaml" /opt/miniconda3/envs/stack/bin/pytest -s -v tests/client-sdk/inference` Before: ``` ________________________________________________________________________________________ test_completion_structured_output __________________________________________________________________________________________ tests/client-sdk/inference/test_inference.py:174: in test_completion_structured_output answer = AnswerFormat.model_validate_json(response.content) E pydantic_core._pydantic_core.ValidationError: 1 validation error for AnswerFormat E Invalid JSON: expected value at line 1 column 2 [type=json_invalid, input_value=' The year he retired, he...5\n\nThe best answer is', input_type=str] E For further information visit https://errors.pydantic.dev/2.10/v/json_invalid ``` After: test consistently passes ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- llama_stack/providers/remote/inference/ollama/ollama.py | 1 + llama_stack/providers/tests/inference/test_text_inference.py | 1 - tests/client-sdk/inference/test_inference.py | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llama_stack/providers/remote/inference/ollama/ollama.py b/llama_stack/providers/remote/inference/ollama/ollama.py index 38721ea22..6811d435b 100644 --- a/llama_stack/providers/remote/inference/ollama/ollama.py +++ b/llama_stack/providers/remote/inference/ollama/ollama.py @@ -172,6 +172,7 @@ class OllamaInferenceAdapter(Inference, ModelsProtocolPrivate): model=model.provider_resource_id, content=content, sampling_params=sampling_params, + response_format=response_format, stream=stream, logprobs=logprobs, ) diff --git a/llama_stack/providers/tests/inference/test_text_inference.py b/llama_stack/providers/tests/inference/test_text_inference.py index 1243881b9..cbc8232c8 100644 --- a/llama_stack/providers/tests/inference/test_text_inference.py +++ b/llama_stack/providers/tests/inference/test_text_inference.py @@ -208,7 +208,6 @@ class TestInference: assert not chunk.logprobs, "Logprobs should be empty" @pytest.mark.asyncio(loop_scope="session") - @pytest.mark.skip("This test is not quite robust") async def test_completion_structured_output(self, inference_model, inference_stack): inference_impl, _ = inference_stack diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py index ac2c4ce38..f161c7509 100644 --- a/tests/client-sdk/inference/test_inference.py +++ b/tests/client-sdk/inference/test_inference.py @@ -11,7 +11,7 @@ import pytest from pydantic import BaseModel PROVIDER_TOOL_PROMPT_FORMAT = { - "remote::ollama": "python_list", + "remote::ollama": "json", "remote::together": "json", "remote::fireworks": "json", } From edf56884a7f41e33f7a7a9843157be769a986aad Mon Sep 17 00:00:00 2001 From: Sixian Yi Date: Tue, 21 Jan 2025 21:18:23 -0800 Subject: [PATCH 067/107] add pytest option to generate a functional report for distribution (#833) # What does this PR do? add pytest option (`--report`) to support generating a functional report for llama stack distribution ## Test Plan ``` export LLAMA_STACK_CONFIG=./llama_stack/templates/fireworks/run.yaml /opt/miniconda3/envs/stack/bin/pytest -s -v tests/client-sdk/ --report ``` See a report file was generated under `./llama_stack/templates/fireworks/report.md` ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- llama_stack/templates/fireworks/report.md | 45 ++++ tests/client-sdk/agents/test_agents.py | 6 +- tests/client-sdk/conftest.py | 16 ++ tests/client-sdk/inference/test_inference.py | 6 +- tests/client-sdk/metadata.py | 50 +++++ tests/client-sdk/report.py | 207 +++++++++++++++++++ 6 files changed, 324 insertions(+), 6 deletions(-) create mode 100644 llama_stack/templates/fireworks/report.md create mode 100644 tests/client-sdk/metadata.py create mode 100644 tests/client-sdk/report.py diff --git a/llama_stack/templates/fireworks/report.md b/llama_stack/templates/fireworks/report.md new file mode 100644 index 000000000..5ca65c62e --- /dev/null +++ b/llama_stack/templates/fireworks/report.md @@ -0,0 +1,45 @@ +# Report for fireworks distribution + +## Supported Models: +| Model Descriptor | fireworks | +|:---|:---| +| Llama-3-8B-Instruct | ❌ | +| Llama-3-70B-Instruct | ❌ | +| Llama3.1-8B-Instruct | ✅ | +| Llama3.1-70B-Instruct | ✅ | +| Llama3.1-405B-Instruct | ✅ | +| Llama3.2-1B-Instruct | ✅ | +| Llama3.2-3B-Instruct | ✅ | +| Llama3.2-11B-Vision-Instruct | ✅ | +| Llama3.2-90B-Vision-Instruct | ✅ | +| Llama3.3-70B-Instruct | ✅ | +| Llama-Guard-3-11B-Vision | ✅ | +| Llama-Guard-3-1B | ❌ | +| Llama-Guard-3-8B | ✅ | +| Llama-Guard-2-8B | ❌ | + +## Inference: +| Model | API | Capability | Test | Status | +|:----- |:-----|:-----|:-----|:-----| +| Text | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ | +| Vision | /chat_completion | streaming | test_image_chat_completion_streaming | Passed | +| Text | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ✅ | +| Vision | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | Passed | +| Text | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ✅ | +| Text | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ✅ | +| Text | /completion | streaming | test_text_completion_streaming | ✅ | +| Text | /completion | non_streaming | test_text_completion_non_streaming | ✅ | +| Text | /completion | structured_output | test_text_completion_structured_output | ✅ | + +## Memory: +| API | Capability | Test | Status | +|:-----|:-----|:-----|:-----| +| /insert, /query | inline | test_memory_bank_insert_inline_and_query | ❌ | +| /insert, /query | url | test_memory_bank_insert_from_url_and_query | ❌ | + +## Agents: +| API | Capability | Test | Status | +|:-----|:-----|:-----|:-----| +| create_agent_turn | rag | test_rag_agent | ❌ | +| create_agent_turn | custom_tool | test_custom_tool | ✅ | +| create_agent_turn | code_execution | test_code_execution | ❌ | diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py index bfe279e24..36fe2843d 100644 --- a/tests/client-sdk/agents/test_agents.py +++ b/tests/client-sdk/agents/test_agents.py @@ -80,7 +80,7 @@ class TestClientTool(ClientTool): @pytest.fixture(scope="session") -def model_id(llama_stack_client): +def text_model_id(llama_stack_client): available_models = [ model.identifier for model in llama_stack_client.models.list() @@ -92,14 +92,14 @@ def model_id(llama_stack_client): @pytest.fixture(scope="session") -def agent_config(llama_stack_client, model_id): +def agent_config(llama_stack_client, text_model_id): available_shields = [ shield.identifier for shield in llama_stack_client.shields.list() ] available_shields = available_shields[:1] print(f"Using shield: {available_shields}") agent_config = AgentConfig( - model=model_id, + model=text_model_id, instructions="You are a helpful assistant", sampling_params={ "strategy": { diff --git a/tests/client-sdk/conftest.py b/tests/client-sdk/conftest.py index b40d54ee5..c19546887 100644 --- a/tests/client-sdk/conftest.py +++ b/tests/client-sdk/conftest.py @@ -10,11 +10,27 @@ import pytest from llama_stack import LlamaStackAsLibraryClient from llama_stack.providers.tests.env import get_env_or_fail from llama_stack_client import LlamaStackClient +from report import Report def pytest_configure(config): config.option.tbstyle = "short" config.option.disable_warnings = True + if config.getoption("--report"): + config.pluginmanager.register(Report()) + + +def pytest_addoption(parser): + parser.addoption( + "--report", + default=False, + action="store_true", + help="Knob to determine if we should generate report, e.g. --output=True", + ) + + +TEXT_MODEL = "meta-llama/Llama-3.1-8B-Instruct" +INFERENCE_MODEL = "meta-llama/Llama-3.2-11B-Vision-Instruct" @pytest.fixture(scope="session") diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py index f161c7509..08c7e1693 100644 --- a/tests/client-sdk/inference/test_inference.py +++ b/tests/client-sdk/inference/test_inference.py @@ -82,7 +82,7 @@ def base64_image_url(): return base64_url -def test_completion_non_streaming(llama_stack_client, text_model_id): +def test_text_completion_non_streaming(llama_stack_client, text_model_id): response = llama_stack_client.inference.completion( content="Complete the sentence using one word: Roses are red, violets are ", stream=False, @@ -94,7 +94,7 @@ def test_completion_non_streaming(llama_stack_client, text_model_id): assert "blue" in response.content.lower().strip() -def test_completion_streaming(llama_stack_client, text_model_id): +def test_text_completion_streaming(llama_stack_client, text_model_id): response = llama_stack_client.inference.completion( content="Complete the sentence using one word: Roses are red, violets are ", stream=True, @@ -147,7 +147,7 @@ def test_completion_log_probs_streaming(llama_stack_client, text_model_id): assert not chunk.logprobs, "Logprobs should be empty" -def test_completion_structured_output( +def test_text_completion_structured_output( llama_stack_client, text_model_id, inference_provider_type ): user_input = """ diff --git a/tests/client-sdk/metadata.py b/tests/client-sdk/metadata.py new file mode 100644 index 000000000..d8d6616c2 --- /dev/null +++ b/tests/client-sdk/metadata.py @@ -0,0 +1,50 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + + +INFERENCE_API_CAPA_TEST_MAP = { + "chat_completion": { + "streaming": [ + "test_text_chat_completion_streaming", + "test_image_chat_completion_streaming", + ], + "non_streaming": [ + "test_image_chat_completion_non_streaming", + "test_text_chat_completion_non_streaming", + ], + "tool_calling": [ + "test_text_chat_completion_with_tool_calling_and_streaming", + "test_text_chat_completion_with_tool_calling_and_non_streaming", + ], + }, + "completion": { + "streaming": ["test_text_completion_streaming"], + "non_streaming": ["test_text_completion_non_streaming"], + "structured_output": ["test_text_completion_structured_output"], + }, +} + +MEMORY_API_TEST_MAP = { + "/insert, /query": { + "inline": ["test_memory_bank_insert_inline_and_query"], + "url": ["test_memory_bank_insert_from_url_and_query"], + } +} + +AGENTS_API_TEST_MAP = { + "create_agent_turn": { + "rag": ["test_rag_agent"], + "custom_tool": ["test_custom_tool"], + "code_execution": ["test_code_execution"], + } +} + + +API_MAPS = { + "inference": INFERENCE_API_CAPA_TEST_MAP, + "memory": MEMORY_API_TEST_MAP, + "agents": AGENTS_API_TEST_MAP, +} diff --git a/tests/client-sdk/report.py b/tests/client-sdk/report.py new file mode 100644 index 000000000..a2ff07e4f --- /dev/null +++ b/tests/client-sdk/report.py @@ -0,0 +1,207 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + + +import os +from collections import defaultdict +from pathlib import Path + +import pytest +from llama_models.datatypes import CoreModelId +from llama_models.sku_list import all_registered_models + +from llama_stack.distribution.library_client import LlamaStackAsLibraryClient +from metadata import API_MAPS + +from pytest import CollectReport + + +SUPPORTED_MODELS = { + "ollama": set( + [ + CoreModelId.llama3_1_8b_instruct.value, + CoreModelId.llama3_1_8b_instruct.value, + CoreModelId.llama3_1_70b_instruct.value, + CoreModelId.llama3_1_70b_instruct.value, + CoreModelId.llama3_1_405b_instruct.value, + CoreModelId.llama3_1_405b_instruct.value, + CoreModelId.llama3_2_1b_instruct.value, + CoreModelId.llama3_2_1b_instruct.value, + CoreModelId.llama3_2_3b_instruct.value, + CoreModelId.llama3_2_3b_instruct.value, + CoreModelId.llama3_2_11b_vision_instruct.value, + CoreModelId.llama3_2_11b_vision_instruct.value, + CoreModelId.llama3_2_90b_vision_instruct.value, + CoreModelId.llama3_2_90b_vision_instruct.value, + CoreModelId.llama3_3_70b_instruct.value, + CoreModelId.llama_guard_3_8b.value, + CoreModelId.llama_guard_3_1b.value, + ] + ), + "fireworks": set( + [ + CoreModelId.llama3_1_8b_instruct.value, + CoreModelId.llama3_1_70b_instruct.value, + CoreModelId.llama3_1_405b_instruct.value, + CoreModelId.llama3_2_1b_instruct.value, + CoreModelId.llama3_2_3b_instruct.value, + CoreModelId.llama3_2_11b_vision_instruct.value, + CoreModelId.llama3_2_90b_vision_instruct.value, + CoreModelId.llama3_3_70b_instruct.value, + CoreModelId.llama_guard_3_8b.value, + CoreModelId.llama_guard_3_11b_vision.value, + ] + ), + "together": set( + [ + CoreModelId.llama3_1_8b_instruct.value, + CoreModelId.llama3_1_70b_instruct.value, + CoreModelId.llama3_1_405b_instruct.value, + CoreModelId.llama3_2_3b_instruct.value, + CoreModelId.llama3_2_11b_vision_instruct.value, + CoreModelId.llama3_2_90b_vision_instruct.value, + CoreModelId.llama3_3_70b_instruct.value, + CoreModelId.llama_guard_3_8b.value, + CoreModelId.llama_guard_3_11b_vision.value, + ] + ), +} + + +class Report: + + def __init__(self): + config_file = os.environ.get("LLAMA_STACK_CONFIG") + if not config_file: + raise ValueError( + "Currently we only support generating report for LlamaStackClientLibrary distributions" + ) + config_path = Path(config_file) + self.output_path = Path(config_path.parent / "report.md") + self.client = LlamaStackAsLibraryClient( + config_file, + provider_data=None, + skip_logger_removal=True, + ) + self.image_name = self.client.async_client.config.image_name + self.report_data = defaultdict(dict) + # test function -> test nodeid + self.test_data = dict() + self.test_name_to_nodeid = defaultdict(list) + + @pytest.hookimpl(tryfirst=True) + def pytest_runtest_logreport(self, report): + # This hook is called in several phases, including setup, call and teardown + # The test is considered failed / error if any of the outcomes is not "Passed" + outcome = self._process_outcome(report) + if report.nodeid not in self.test_data: + self.test_data[report.nodeid] = outcome + elif self.test_data[report.nodeid] != outcome and outcome != "Passed": + self.test_data[report.nodeid] = outcome + + def pytest_sessionfinish(self, session): + report = [] + report.append(f"# Report for {self.image_name} distribution") + report.append("\n## Supported Models: ") + + header = f"| Model Descriptor | {self.image_name} |" + dividor = "|:---|:---|" + + report.append(header) + report.append(dividor) + + rows = [] + for model in all_registered_models(): + if ( + "Instruct" not in model.core_model_id.value + and "Guard" not in model.core_model_id.value + ) or (model.variant): + continue + row = f"| {model.core_model_id.value} |" + if model.core_model_id.value in SUPPORTED_MODELS[self.image_name]: + row += " ✅ |" + else: + row += " ❌ |" + rows.append(row) + report.extend(rows) + + report.append("\n## Inference: ") + test_table = [ + "| Model | API | Capability | Test | Status |", + "|:----- |:-----|:-----|:-----|:-----|", + ] + for api, capa_map in API_MAPS["inference"].items(): + for capa, tests in capa_map.items(): + vision_tests = filter(lambda test_name: "image" in test_name, tests) + text_tests = filter(lambda test_name: "text" in test_name, tests) + + for test_name in text_tests: + test_nodeids = self.test_name_to_nodeid[test_name] + assert len(test_nodeids) > 0 + # There might be more than one parametrizations for the same test function. We take + # the result of the first one for now. Ideally we should mark the test as failed if + # any of the parametrizations failed. + test_table.append( + f"| Text | /{api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |" + ) + + for test_name in vision_tests: + test_nodeids = self.test_name_to_nodeid[test_name] + assert len(test_nodeids) > 0 + test_table.append( + f"| Vision | /{api} | {capa} | {test_name} | {self.test_data[test_nodeids[0]]} |" + ) + + report.extend(test_table) + + for api_group in ["memory", "agents"]: + api_capitalized = api_group.capitalize() + report.append(f"\n## {api_capitalized}: ") + test_table = [ + "| API | Capability | Test | Status |", + "|:-----|:-----|:-----|:-----|", + ] + for api, capa_map in API_MAPS[api_group].items(): + for capa, tests in capa_map.items(): + for test_name in tests: + test_nodeids = self.test_name_to_nodeid[test_name] + assert len(test_nodeids) > 0 + test_table.append( + f"| {api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |" + ) + report.extend(test_table) + output_file = self.output_path + output_file.write_text("\n".join(report)) + print(f"\nReport generated: {output_file.absolute()}") + + def pytest_runtest_makereport(self, item, call): + func_name = getattr(item, "originalname", item.name) + self.test_name_to_nodeid[func_name].append(item.nodeid) + + def _print_result_icon(self, result): + if result == "Passed": + return "✅" + elif result == "Failed" or result == "Error": + return "❌" + else: + # result == "Skipped": + return "⏭️" + + def _process_outcome(self, report: CollectReport): + if self._is_error(report): + return "Error" + if hasattr(report, "wasxfail"): + if report.outcome in ["passed", "failed"]: + return "XPassed" + if report.outcome == "skipped": + return "XFailed" + return report.outcome.capitalize() + + def _is_error(self, report: CollectReport): + return ( + report.when in ["setup", "teardown", "collect"] + and report.outcome == "failed" + ) From 35a00d004ab9063d8bc286971d0a6a2010d88e3e Mon Sep 17 00:00:00 2001 From: Sixian Yi Date: Tue, 21 Jan 2025 21:44:06 -0800 Subject: [PATCH 068/107] bug fix for distro report generation (#836) # What does this PR do? Minor bug fix and simplify code - [ ] Addresses issue (#issue) ## Test Plan See the updated `llama_stack/templates/fireworks/report.md` ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- llama_stack/templates/fireworks/report.md | 4 ++-- tests/client-sdk/conftest.py | 1 - tests/client-sdk/report.py | 15 +++------------ 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/llama_stack/templates/fireworks/report.md b/llama_stack/templates/fireworks/report.md index 5ca65c62e..ac6fab6eb 100644 --- a/llama_stack/templates/fireworks/report.md +++ b/llama_stack/templates/fireworks/report.md @@ -22,9 +22,9 @@ | Model | API | Capability | Test | Status | |:----- |:-----|:-----|:-----|:-----| | Text | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ | -| Vision | /chat_completion | streaming | test_image_chat_completion_streaming | Passed | +| Vision | /chat_completion | streaming | test_image_chat_completion_streaming | ✅ | +| Vision | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | ✅ | | Text | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ✅ | -| Vision | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | Passed | | Text | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ✅ | | Text | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ✅ | | Text | /completion | streaming | test_text_completion_streaming | ✅ | diff --git a/tests/client-sdk/conftest.py b/tests/client-sdk/conftest.py index c19546887..0b5324c0e 100644 --- a/tests/client-sdk/conftest.py +++ b/tests/client-sdk/conftest.py @@ -32,7 +32,6 @@ def pytest_addoption(parser): TEXT_MODEL = "meta-llama/Llama-3.1-8B-Instruct" INFERENCE_MODEL = "meta-llama/Llama-3.2-11B-Vision-Instruct" - @pytest.fixture(scope="session") def provider_data(): # check env for tavily secret, brave secret and inject all into provider data diff --git a/tests/client-sdk/report.py b/tests/client-sdk/report.py index a2ff07e4f..22aa98935 100644 --- a/tests/client-sdk/report.py +++ b/tests/client-sdk/report.py @@ -135,24 +135,15 @@ class Report: ] for api, capa_map in API_MAPS["inference"].items(): for capa, tests in capa_map.items(): - vision_tests = filter(lambda test_name: "image" in test_name, tests) - text_tests = filter(lambda test_name: "text" in test_name, tests) - - for test_name in text_tests: + for test_name in tests: + model_type = "Text" if "text" in test_name else "Vision" test_nodeids = self.test_name_to_nodeid[test_name] assert len(test_nodeids) > 0 # There might be more than one parametrizations for the same test function. We take # the result of the first one for now. Ideally we should mark the test as failed if # any of the parametrizations failed. test_table.append( - f"| Text | /{api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |" - ) - - for test_name in vision_tests: - test_nodeids = self.test_name_to_nodeid[test_name] - assert len(test_nodeids) > 0 - test_table.append( - f"| Vision | /{api} | {capa} | {test_name} | {self.test_data[test_nodeids[0]]} |" + f"| {model_type} | /{api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |" ) report.extend(test_table) From 3ae8585b6521050b3f3978a22f1122760c7fe7ae Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 09:59:30 -0800 Subject: [PATCH 069/107] [memory refactor][1/n] Rename Memory -> VectorIO, MemoryBanks -> VectorDBs (#828) See https://github.com/meta-llama/llama-stack/issues/827 for the broader design. This is the first part: - delete other kinds of memory banks (keyvalue, keyword, graph) for now; we will introduce a keyvalue store API as part of this design but not use it in the RAG tool yet. - renaming of the APIs --- llama_stack/apis/agents/agents.py | 3 - llama_stack/apis/datatypes.py | 4 +- llama_stack/apis/memory_banks/memory_banks.py | 161 ------------------ llama_stack/apis/resource.py | 2 +- .../{memory_banks => vector_dbs}/__init__.py | 2 +- llama_stack/apis/vector_dbs/vector_dbs.py | 66 +++++++ .../apis/{memory => vector_io}/__init__.py | 2 +- .../memory.py => vector_io/vector_io.py} | 40 ++--- llama_stack/distribution/datatypes.py | 12 +- llama_stack/distribution/distribution.py | 4 +- llama_stack/distribution/resolver.py | 12 +- .../distribution/routers/routing_tables.py | 95 +++++------ llama_stack/distribution/stack.py | 10 +- llama_stack/providers/datatypes.py | 8 +- .../inline/{memory => vector_io}/__init__.py | 0 .../{memory => vector_io}/chroma/__init__.py | 0 .../{memory => vector_io}/chroma/config.py | 0 .../{memory => vector_io}/faiss/__init__.py | 0 .../{memory => vector_io}/faiss/config.py | 0 .../{memory => vector_io}/faiss/faiss.py | 0 .../registry/{memory.py => vector_io.py} | 50 +++--- .../remote/{memory => vector_io}/__init__.py | 0 .../{memory => vector_io}/chroma/__init__.py | 0 .../{memory => vector_io}/chroma/chroma.py | 0 .../{memory => vector_io}/chroma/config.py | 0 .../pgvector/__init__.py | 0 .../{memory => vector_io}/pgvector/config.py | 0 .../pgvector/pgvector.py | 0 .../{memory => vector_io}/qdrant/__init__.py | 0 .../{memory => vector_io}/qdrant/config.py | 0 .../{memory => vector_io}/qdrant/qdrant.py | 0 .../{memory => vector_io}/sample/__init__.py | 0 .../{memory => vector_io}/sample/config.py | 0 .../{memory => vector_io}/sample/sample.py | 0 .../weaviate/__init__.py | 0 .../{memory => vector_io}/weaviate/config.py | 0 .../weaviate/weaviate.py | 0 37 files changed, 175 insertions(+), 296 deletions(-) delete mode 100644 llama_stack/apis/memory_banks/memory_banks.py rename llama_stack/apis/{memory_banks => vector_dbs}/__init__.py (81%) create mode 100644 llama_stack/apis/vector_dbs/vector_dbs.py rename llama_stack/apis/{memory => vector_io}/__init__.py (82%) rename llama_stack/apis/{memory/memory.py => vector_io/vector_io.py} (61%) rename llama_stack/providers/inline/{memory => vector_io}/__init__.py (100%) rename llama_stack/providers/inline/{memory => vector_io}/chroma/__init__.py (100%) rename llama_stack/providers/inline/{memory => vector_io}/chroma/config.py (100%) rename llama_stack/providers/inline/{memory => vector_io}/faiss/__init__.py (100%) rename llama_stack/providers/inline/{memory => vector_io}/faiss/config.py (100%) rename llama_stack/providers/inline/{memory => vector_io}/faiss/faiss.py (100%) rename llama_stack/providers/registry/{memory.py => vector_io.py} (64%) rename llama_stack/providers/remote/{memory => vector_io}/__init__.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/chroma/__init__.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/chroma/chroma.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/chroma/config.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/pgvector/__init__.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/pgvector/config.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/pgvector/pgvector.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/qdrant/__init__.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/qdrant/config.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/qdrant/qdrant.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/sample/__init__.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/sample/config.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/sample/sample.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/weaviate/__init__.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/weaviate/config.py (100%) rename llama_stack/providers/remote/{memory => vector_io}/weaviate/weaviate.py (100%) diff --git a/llama_stack/apis/agents/agents.py b/llama_stack/apis/agents/agents.py index 63d0920fb..20cb8f828 100644 --- a/llama_stack/apis/agents/agents.py +++ b/llama_stack/apis/agents/agents.py @@ -33,7 +33,6 @@ from llama_stack.apis.inference import ( ToolResponseMessage, UserMessage, ) -from llama_stack.apis.memory import MemoryBank from llama_stack.apis.safety import SafetyViolation from llama_stack.apis.tools import ToolDef from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol @@ -133,8 +132,6 @@ class Session(BaseModel): turns: List[Turn] started_at: datetime - memory_bank: Optional[MemoryBank] = None - class AgentToolGroupWithArgs(BaseModel): name: str diff --git a/llama_stack/apis/datatypes.py b/llama_stack/apis/datatypes.py index 52c429a2b..ccc395b80 100644 --- a/llama_stack/apis/datatypes.py +++ b/llama_stack/apis/datatypes.py @@ -14,7 +14,7 @@ class Api(Enum): inference = "inference" safety = "safety" agents = "agents" - memory = "memory" + vector_io = "vector_io" datasetio = "datasetio" scoring = "scoring" eval = "eval" @@ -25,7 +25,7 @@ class Api(Enum): models = "models" shields = "shields" - memory_banks = "memory_banks" + vector_dbs = "vector_dbs" datasets = "datasets" scoring_functions = "scoring_functions" eval_tasks = "eval_tasks" diff --git a/llama_stack/apis/memory_banks/memory_banks.py b/llama_stack/apis/memory_banks/memory_banks.py deleted file mode 100644 index ec8ba824b..000000000 --- a/llama_stack/apis/memory_banks/memory_banks.py +++ /dev/null @@ -1,161 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the terms described in the LICENSE file in -# the root directory of this source tree. - -from enum import Enum -from typing import ( - Annotated, - List, - Literal, - Optional, - Protocol, - runtime_checkable, - Union, -) - -from llama_models.schema_utils import json_schema_type, register_schema, webmethod -from pydantic import BaseModel, Field - -from llama_stack.apis.resource import Resource, ResourceType -from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol - - -@json_schema_type -class MemoryBankType(Enum): - vector = "vector" - keyvalue = "keyvalue" - keyword = "keyword" - graph = "graph" - - -# define params for each type of memory bank, this leads to a tagged union -# accepted as input from the API or from the config. -@json_schema_type -class VectorMemoryBankParams(BaseModel): - memory_bank_type: Literal[MemoryBankType.vector.value] = MemoryBankType.vector.value - embedding_model: str - chunk_size_in_tokens: int - overlap_size_in_tokens: Optional[int] = None - - -@json_schema_type -class KeyValueMemoryBankParams(BaseModel): - memory_bank_type: Literal[MemoryBankType.keyvalue.value] = ( - MemoryBankType.keyvalue.value - ) - - -@json_schema_type -class KeywordMemoryBankParams(BaseModel): - memory_bank_type: Literal[MemoryBankType.keyword.value] = ( - MemoryBankType.keyword.value - ) - - -@json_schema_type -class GraphMemoryBankParams(BaseModel): - memory_bank_type: Literal[MemoryBankType.graph.value] = MemoryBankType.graph.value - - -BankParams = Annotated[ - Union[ - VectorMemoryBankParams, - KeyValueMemoryBankParams, - KeywordMemoryBankParams, - GraphMemoryBankParams, - ], - Field(discriminator="memory_bank_type"), -] - - -# Some common functionality for memory banks. -class MemoryBankResourceMixin(Resource): - type: Literal[ResourceType.memory_bank.value] = ResourceType.memory_bank.value - - @property - def memory_bank_id(self) -> str: - return self.identifier - - @property - def provider_memory_bank_id(self) -> str: - return self.provider_resource_id - - -@json_schema_type -class VectorMemoryBank(MemoryBankResourceMixin): - memory_bank_type: Literal[MemoryBankType.vector.value] = MemoryBankType.vector.value - embedding_model: str - chunk_size_in_tokens: int - embedding_dimension: Optional[int] = 384 # default to minilm-l6-v2 - overlap_size_in_tokens: Optional[int] = None - - -@json_schema_type -class KeyValueMemoryBank(MemoryBankResourceMixin): - memory_bank_type: Literal[MemoryBankType.keyvalue.value] = ( - MemoryBankType.keyvalue.value - ) - - -# TODO: KeyValue and Keyword are so similar in name, oof. Get a better naming convention. -@json_schema_type -class KeywordMemoryBank(MemoryBankResourceMixin): - memory_bank_type: Literal[MemoryBankType.keyword.value] = ( - MemoryBankType.keyword.value - ) - - -@json_schema_type -class GraphMemoryBank(MemoryBankResourceMixin): - memory_bank_type: Literal[MemoryBankType.graph.value] = MemoryBankType.graph.value - - -MemoryBank = register_schema( - Annotated[ - Union[ - VectorMemoryBank, - KeyValueMemoryBank, - KeywordMemoryBank, - GraphMemoryBank, - ], - Field(discriminator="memory_bank_type"), - ], - name="MemoryBank", -) - - -class MemoryBankInput(BaseModel): - memory_bank_id: str - params: BankParams - provider_memory_bank_id: Optional[str] = None - - -class ListMemoryBanksResponse(BaseModel): - data: List[MemoryBank] - - -@runtime_checkable -@trace_protocol -class MemoryBanks(Protocol): - @webmethod(route="/memory-banks", method="GET") - async def list_memory_banks(self) -> ListMemoryBanksResponse: ... - - @webmethod(route="/memory-banks/{memory_bank_id}", method="GET") - async def get_memory_bank( - self, - memory_bank_id: str, - ) -> Optional[MemoryBank]: ... - - @webmethod(route="/memory-banks", method="POST") - async def register_memory_bank( - self, - memory_bank_id: str, - params: BankParams, - provider_id: Optional[str] = None, - provider_memory_bank_id: Optional[str] = None, - ) -> MemoryBank: ... - - @webmethod(route="/memory-banks/{memory_bank_id}", method="DELETE") - async def unregister_memory_bank(self, memory_bank_id: str) -> None: ... diff --git a/llama_stack/apis/resource.py b/llama_stack/apis/resource.py index a85f5a31c..dfe3ddb24 100644 --- a/llama_stack/apis/resource.py +++ b/llama_stack/apis/resource.py @@ -14,7 +14,7 @@ from pydantic import BaseModel, Field class ResourceType(Enum): model = "model" shield = "shield" - memory_bank = "memory_bank" + vector_db = "vector_db" dataset = "dataset" scoring_function = "scoring_function" eval_task = "eval_task" diff --git a/llama_stack/apis/memory_banks/__init__.py b/llama_stack/apis/vector_dbs/__init__.py similarity index 81% rename from llama_stack/apis/memory_banks/__init__.py rename to llama_stack/apis/vector_dbs/__init__.py index 7511677ab..158241a6d 100644 --- a/llama_stack/apis/memory_banks/__init__.py +++ b/llama_stack/apis/vector_dbs/__init__.py @@ -4,4 +4,4 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -from .memory_banks import * # noqa: F401 F403 +from .vector_dbs import * # noqa: F401 F403 diff --git a/llama_stack/apis/vector_dbs/vector_dbs.py b/llama_stack/apis/vector_dbs/vector_dbs.py new file mode 100644 index 000000000..4b782e2d5 --- /dev/null +++ b/llama_stack/apis/vector_dbs/vector_dbs.py @@ -0,0 +1,66 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +from typing import List, Literal, Optional, Protocol, runtime_checkable + +from llama_models.schema_utils import json_schema_type, webmethod +from pydantic import BaseModel + +from llama_stack.apis.resource import Resource, ResourceType +from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol + + +@json_schema_type +class VectorDB(Resource): + type: Literal[ResourceType.vector_db.value] = ResourceType.vector_db.value + + embedding_model: str + embedding_dimension: int + + @property + def vector_db_id(self) -> str: + return self.identifier + + @property + def provider_vector_db_id(self) -> str: + return self.provider_resource_id + + +class VectorDBInput(BaseModel): + vector_db_id: str + embedding_model: str + embedding_dimension: int + provider_vector_db_id: Optional[str] = None + + +class ListVectorDBsResponse(BaseModel): + data: List[VectorDB] + + +@runtime_checkable +@trace_protocol +class VectorDBs(Protocol): + @webmethod(route="/vector-dbs", method="GET") + async def list_vector_dbs(self) -> ListVectorDBsResponse: ... + + @webmethod(route="/vector-dbs/{vector_db_id}", method="GET") + async def get_vector_db( + self, + vector_db_id: str, + ) -> Optional[VectorDB]: ... + + @webmethod(route="/vector-dbs", method="POST") + async def register_vector_db( + self, + vector_db_id: str, + embedding_model: str, + embedding_dimension: Optional[int] = 384, + provider_id: Optional[str] = None, + provider_vector_db_id: Optional[str] = None, + ) -> VectorDB: ... + + @webmethod(route="/vector-dbs/{vector_db_id}", method="DELETE") + async def unregister_vector_db(self, vector_db_id: str) -> None: ... diff --git a/llama_stack/apis/memory/__init__.py b/llama_stack/apis/vector_io/__init__.py similarity index 82% rename from llama_stack/apis/memory/__init__.py rename to llama_stack/apis/vector_io/__init__.py index 260862228..3fe4fa4b6 100644 --- a/llama_stack/apis/memory/__init__.py +++ b/llama_stack/apis/vector_io/__init__.py @@ -4,4 +4,4 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -from .memory import * # noqa: F401 F403 +from .vector_io import * # noqa: F401 F403 diff --git a/llama_stack/apis/memory/memory.py b/llama_stack/apis/vector_io/vector_io.py similarity index 61% rename from llama_stack/apis/memory/memory.py rename to llama_stack/apis/vector_io/vector_io.py index 6e6fcf697..5371b8918 100644 --- a/llama_stack/apis/memory/memory.py +++ b/llama_stack/apis/vector_io/vector_io.py @@ -13,55 +13,45 @@ from typing import Any, Dict, List, Optional, Protocol, runtime_checkable from llama_models.schema_utils import json_schema_type, webmethod from pydantic import BaseModel, Field -from llama_stack.apis.common.content_types import URL from llama_stack.apis.inference import InterleavedContent -from llama_stack.apis.memory_banks import MemoryBank +from llama_stack.apis.vector_dbs import VectorDB from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol -@json_schema_type -class MemoryBankDocument(BaseModel): - document_id: str - content: InterleavedContent | URL - mime_type: str | None = None - metadata: Dict[str, Any] = Field(default_factory=dict) - - class Chunk(BaseModel): content: InterleavedContent - token_count: int - document_id: str + metadata: Dict[str, Any] = Field(default_factory=dict) @json_schema_type -class QueryDocumentsResponse(BaseModel): +class QueryChunksResponse(BaseModel): chunks: List[Chunk] scores: List[float] -class MemoryBankStore(Protocol): - def get_memory_bank(self, bank_id: str) -> Optional[MemoryBank]: ... +class VectorDBStore(Protocol): + def get_vector_db(self, vector_db_id: str) -> Optional[VectorDB]: ... @runtime_checkable @trace_protocol -class Memory(Protocol): - memory_bank_store: MemoryBankStore +class VectorIO(Protocol): + vector_db_store: VectorDBStore # this will just block now until documents are inserted, but it should # probably return a Job instance which can be polled for completion - @webmethod(route="/memory/insert", method="POST") - async def insert_documents( + @webmethod(route="/vector-io/insert", method="POST") + async def insert_chunks( self, - bank_id: str, - documents: List[MemoryBankDocument], + vector_db_id: str, + chunks: List[Chunk], ttl_seconds: Optional[int] = None, ) -> None: ... - @webmethod(route="/memory/query", method="POST") - async def query_documents( + @webmethod(route="/vector-io/query", method="POST") + async def query_chunks( self, - bank_id: str, + vector_db_id: str, query: InterleavedContent, params: Optional[Dict[str, Any]] = None, - ) -> QueryDocumentsResponse: ... + ) -> QueryChunksResponse: ... diff --git a/llama_stack/distribution/datatypes.py b/llama_stack/distribution/datatypes.py index c1a91cf6c..99ffeb346 100644 --- a/llama_stack/distribution/datatypes.py +++ b/llama_stack/distribution/datatypes.py @@ -13,14 +13,14 @@ from llama_stack.apis.datasets import Dataset, DatasetInput from llama_stack.apis.eval import Eval from llama_stack.apis.eval_tasks import EvalTask, EvalTaskInput from llama_stack.apis.inference import Inference -from llama_stack.apis.memory import Memory -from llama_stack.apis.memory_banks import MemoryBank, MemoryBankInput from llama_stack.apis.models import Model, ModelInput from llama_stack.apis.safety import Safety from llama_stack.apis.scoring import Scoring from llama_stack.apis.scoring_functions import ScoringFn, ScoringFnInput from llama_stack.apis.shields import Shield, ShieldInput from llama_stack.apis.tools import Tool, ToolGroup, ToolGroupInput, ToolRuntime +from llama_stack.apis.vector_dbs import VectorDB, VectorDBInput +from llama_stack.apis.vector_io import VectorIO from llama_stack.providers.datatypes import Api, ProviderSpec from llama_stack.providers.utils.kvstore.config import KVStoreConfig @@ -34,7 +34,7 @@ RoutingKey = Union[str, List[str]] RoutableObject = Union[ Model, Shield, - MemoryBank, + VectorDB, Dataset, ScoringFn, EvalTask, @@ -47,7 +47,7 @@ RoutableObjectWithProvider = Annotated[ Union[ Model, Shield, - MemoryBank, + VectorDB, Dataset, ScoringFn, EvalTask, @@ -60,7 +60,7 @@ RoutableObjectWithProvider = Annotated[ RoutedProtocol = Union[ Inference, Safety, - Memory, + VectorIO, DatasetIO, Scoring, Eval, @@ -153,7 +153,7 @@ a default SQLite store will be used.""", # registry of "resources" in the distribution models: List[ModelInput] = Field(default_factory=list) shields: List[ShieldInput] = Field(default_factory=list) - memory_banks: List[MemoryBankInput] = Field(default_factory=list) + vector_dbs: List[VectorDBInput] = Field(default_factory=list) datasets: List[DatasetInput] = Field(default_factory=list) scoring_fns: List[ScoringFnInput] = Field(default_factory=list) eval_tasks: List[EvalTaskInput] = Field(default_factory=list) diff --git a/llama_stack/distribution/distribution.py b/llama_stack/distribution/distribution.py index 4183d92cd..b02d0fb6c 100644 --- a/llama_stack/distribution/distribution.py +++ b/llama_stack/distribution/distribution.py @@ -32,8 +32,8 @@ def builtin_automatically_routed_apis() -> List[AutoRoutedApiInfo]: router_api=Api.safety, ), AutoRoutedApiInfo( - routing_table_api=Api.memory_banks, - router_api=Api.memory, + routing_table_api=Api.vector_dbs, + router_api=Api.vector_io, ), AutoRoutedApiInfo( routing_table_api=Api.datasets, diff --git a/llama_stack/distribution/resolver.py b/llama_stack/distribution/resolver.py index 204555b16..bd5a9ae98 100644 --- a/llama_stack/distribution/resolver.py +++ b/llama_stack/distribution/resolver.py @@ -15,8 +15,6 @@ from llama_stack.apis.eval import Eval from llama_stack.apis.eval_tasks import EvalTasks from llama_stack.apis.inference import Inference from llama_stack.apis.inspect import Inspect -from llama_stack.apis.memory import Memory -from llama_stack.apis.memory_banks import MemoryBanks from llama_stack.apis.models import Models from llama_stack.apis.post_training import PostTraining from llama_stack.apis.safety import Safety @@ -25,6 +23,8 @@ from llama_stack.apis.scoring_functions import ScoringFunctions from llama_stack.apis.shields import Shields from llama_stack.apis.telemetry import Telemetry from llama_stack.apis.tools import ToolGroups, ToolRuntime +from llama_stack.apis.vector_dbs import VectorDBs +from llama_stack.apis.vector_io import VectorIO from llama_stack.distribution.client import get_client_impl from llama_stack.distribution.datatypes import ( AutoRoutedProviderSpec, @@ -40,7 +40,6 @@ from llama_stack.providers.datatypes import ( DatasetsProtocolPrivate, EvalTasksProtocolPrivate, InlineProviderSpec, - MemoryBanksProtocolPrivate, ModelsProtocolPrivate, ProviderSpec, RemoteProviderConfig, @@ -48,6 +47,7 @@ from llama_stack.providers.datatypes import ( ScoringFunctionsProtocolPrivate, ShieldsProtocolPrivate, ToolsProtocolPrivate, + VectorDBsProtocolPrivate, ) log = logging.getLogger(__name__) @@ -62,8 +62,8 @@ def api_protocol_map() -> Dict[Api, Any]: Api.agents: Agents, Api.inference: Inference, Api.inspect: Inspect, - Api.memory: Memory, - Api.memory_banks: MemoryBanks, + Api.vector_io: VectorIO, + Api.vector_dbs: VectorDBs, Api.models: Models, Api.safety: Safety, Api.shields: Shields, @@ -84,7 +84,7 @@ def additional_protocols_map() -> Dict[Api, Any]: return { Api.inference: (ModelsProtocolPrivate, Models, Api.models), Api.tool_groups: (ToolsProtocolPrivate, ToolGroups, Api.tool_groups), - Api.memory: (MemoryBanksProtocolPrivate, MemoryBanks, Api.memory_banks), + Api.vector_io: (VectorDBsProtocolPrivate, VectorDBs, Api.vector_dbs), Api.safety: (ShieldsProtocolPrivate, Shields, Api.shields), Api.datasetio: (DatasetsProtocolPrivate, Datasets, Api.datasets), Api.scoring: ( diff --git a/llama_stack/distribution/routers/routing_tables.py b/llama_stack/distribution/routers/routing_tables.py index 889bd4624..1d035d878 100644 --- a/llama_stack/distribution/routers/routing_tables.py +++ b/llama_stack/distribution/routers/routing_tables.py @@ -12,13 +12,6 @@ from llama_stack.apis.common.content_types import URL from llama_stack.apis.common.type_system import ParamType from llama_stack.apis.datasets import Dataset, Datasets, ListDatasetsResponse from llama_stack.apis.eval_tasks import EvalTask, EvalTasks, ListEvalTasksResponse -from llama_stack.apis.memory_banks import ( - BankParams, - ListMemoryBanksResponse, - MemoryBank, - MemoryBanks, - MemoryBankType, -) from llama_stack.apis.models import ListModelsResponse, Model, Models, ModelType from llama_stack.apis.resource import ResourceType from llama_stack.apis.scoring_functions import ( @@ -36,6 +29,7 @@ from llama_stack.apis.tools import ( ToolGroups, ToolHost, ) +from llama_stack.apis.vector_dbs import ListVectorDBsResponse, VectorDB, VectorDBs from llama_stack.distribution.datatypes import ( RoutableObject, RoutableObjectWithProvider, @@ -59,8 +53,8 @@ async def register_object_with_provider(obj: RoutableObject, p: Any) -> Routable return await p.register_model(obj) elif api == Api.safety: return await p.register_shield(obj) - elif api == Api.memory: - return await p.register_memory_bank(obj) + elif api == Api.vector_io: + return await p.register_vector_db(obj) elif api == Api.datasetio: return await p.register_dataset(obj) elif api == Api.scoring: @@ -75,8 +69,8 @@ async def register_object_with_provider(obj: RoutableObject, p: Any) -> Routable async def unregister_object_from_provider(obj: RoutableObject, p: Any) -> None: api = get_impl_api(p) - if api == Api.memory: - return await p.unregister_memory_bank(obj.identifier) + if api == Api.vector_io: + return await p.unregister_vector_db(obj.identifier) elif api == Api.inference: return await p.unregister_model(obj.identifier) elif api == Api.datasetio: @@ -120,8 +114,8 @@ class CommonRoutingTableImpl(RoutingTable): p.model_store = self elif api == Api.safety: p.shield_store = self - elif api == Api.memory: - p.memory_bank_store = self + elif api == Api.vector_io: + p.vector_db_store = self elif api == Api.datasetio: p.dataset_store = self elif api == Api.scoring: @@ -145,8 +139,8 @@ class CommonRoutingTableImpl(RoutingTable): return ("Inference", "model") elif isinstance(self, ShieldsRoutingTable): return ("Safety", "shield") - elif isinstance(self, MemoryBanksRoutingTable): - return ("Memory", "memory_bank") + elif isinstance(self, VectorDBsRoutingTable): + return ("VectorIO", "vector_db") elif isinstance(self, DatasetsRoutingTable): return ("DatasetIO", "dataset") elif isinstance(self, ScoringFunctionsRoutingTable): @@ -196,9 +190,6 @@ class CommonRoutingTableImpl(RoutingTable): async def register_object( self, obj: RoutableObjectWithProvider ) -> RoutableObjectWithProvider: - # Get existing objects from registry - existing_obj = await self.dist_registry.get(obj.type, obj.identifier) - # if provider_id is not specified, pick an arbitrary one from existing entries if not obj.provider_id and len(self.impls_by_provider_id) > 0: obj.provider_id = list(self.impls_by_provider_id.keys())[0] @@ -311,22 +302,23 @@ class ShieldsRoutingTable(CommonRoutingTableImpl, Shields): return shield -class MemoryBanksRoutingTable(CommonRoutingTableImpl, MemoryBanks): - async def list_memory_banks(self) -> ListMemoryBanksResponse: - return ListMemoryBanksResponse(data=await self.get_all_with_type("memory_bank")) +class VectorDBsRoutingTable(CommonRoutingTableImpl, VectorDBs): + async def list_vector_dbs(self) -> ListVectorDBsResponse: + return ListVectorDBsResponse(data=await self.get_all_with_type("vector_db")) - async def get_memory_bank(self, memory_bank_id: str) -> Optional[MemoryBank]: - return await self.get_object_by_identifier("memory_bank", memory_bank_id) + async def get_vector_db(self, vector_db_id: str) -> Optional[VectorDB]: + return await self.get_object_by_identifier("vector_db", vector_db_id) - async def register_memory_bank( + async def register_vector_db( self, - memory_bank_id: str, - params: BankParams, + vector_db_id: str, + embedding_model: str, + embedding_dimension: Optional[int] = 384, provider_id: Optional[str] = None, - provider_memory_bank_id: Optional[str] = None, - ) -> MemoryBank: - if provider_memory_bank_id is None: - provider_memory_bank_id = memory_bank_id + provider_vector_db_id: Optional[str] = None, + ) -> VectorDB: + if provider_vector_db_id is None: + provider_vector_db_id = vector_db_id if provider_id is None: # If provider_id not specified, use the only provider if it supports this shield type if len(self.impls_by_provider_id) == 1: @@ -335,44 +327,39 @@ class MemoryBanksRoutingTable(CommonRoutingTableImpl, MemoryBanks): raise ValueError( "No provider specified and multiple providers available. Please specify a provider_id." ) - model = await self.get_object_by_identifier("model", params.embedding_model) + model = await self.get_object_by_identifier("model", embedding_model) if model is None: - if params.embedding_model == "all-MiniLM-L6-v2": + if embedding_model == "all-MiniLM-L6-v2": raise ValueError( "Embeddings are now served via Inference providers. " "Please upgrade your run.yaml to include inline::sentence-transformer as an additional inference provider. " "See https://github.com/meta-llama/llama-stack/blob/main/llama_stack/templates/together/run.yaml for an example." ) else: - raise ValueError(f"Model {params.embedding_model} not found") + raise ValueError(f"Model {embedding_model} not found") if model.model_type != ModelType.embedding: - raise ValueError( - f"Model {params.embedding_model} is not an embedding model" - ) + raise ValueError(f"Model {embedding_model} is not an embedding model") if "embedding_dimension" not in model.metadata: raise ValueError( - f"Model {params.embedding_model} does not have an embedding dimension" + f"Model {embedding_model} does not have an embedding dimension" ) - memory_bank_data = { - "identifier": memory_bank_id, - "type": ResourceType.memory_bank.value, + vector_db_data = { + "identifier": vector_db_id, + "type": ResourceType.vector_db.value, "provider_id": provider_id, - "provider_resource_id": provider_memory_bank_id, - **params.model_dump(), + "provider_resource_id": provider_vector_db_id, + "embedding_model": embedding_model, + "embedding_dimension": model.metadata["embedding_dimension"], } - if params.memory_bank_type == MemoryBankType.vector.value: - memory_bank_data["embedding_dimension"] = model.metadata[ - "embedding_dimension" - ] - memory_bank = TypeAdapter(MemoryBank).validate_python(memory_bank_data) - await self.register_object(memory_bank) - return memory_bank + vector_db = TypeAdapter(VectorDB).validate_python(vector_db_data) + await self.register_object(vector_db) + return vector_db - async def unregister_memory_bank(self, memory_bank_id: str) -> None: - existing_bank = await self.get_memory_bank(memory_bank_id) - if existing_bank is None: - raise ValueError(f"Memory bank {memory_bank_id} not found") - await self.unregister_object(existing_bank) + async def unregister_vector_db(self, vector_db_id: str) -> None: + existing_vector_db = await self.get_vector_db(vector_db_id) + if existing_vector_db is None: + raise ValueError(f"Vector DB {vector_db_id} not found") + await self.unregister_object(existing_vector_db) class DatasetsRoutingTable(CommonRoutingTableImpl, Datasets): diff --git a/llama_stack/distribution/stack.py b/llama_stack/distribution/stack.py index ad7bcd234..180ec0ecc 100644 --- a/llama_stack/distribution/stack.py +++ b/llama_stack/distribution/stack.py @@ -21,8 +21,6 @@ from llama_stack.apis.eval import Eval from llama_stack.apis.eval_tasks import EvalTasks from llama_stack.apis.inference import Inference from llama_stack.apis.inspect import Inspect -from llama_stack.apis.memory import Memory -from llama_stack.apis.memory_banks import MemoryBanks from llama_stack.apis.models import Models from llama_stack.apis.post_training import PostTraining from llama_stack.apis.safety import Safety @@ -32,6 +30,8 @@ from llama_stack.apis.shields import Shields from llama_stack.apis.synthetic_data_generation import SyntheticDataGeneration from llama_stack.apis.telemetry import Telemetry from llama_stack.apis.tools import ToolGroups, ToolRuntime +from llama_stack.apis.vector_dbs import VectorDBs +from llama_stack.apis.vector_io import VectorIO from llama_stack.distribution.datatypes import StackRunConfig from llama_stack.distribution.distribution import get_provider_registry from llama_stack.distribution.resolver import ProviderRegistry, resolve_impls @@ -42,7 +42,7 @@ log = logging.getLogger(__name__) class LlamaStack( - MemoryBanks, + VectorDBs, Inference, BatchInference, Agents, @@ -51,7 +51,7 @@ class LlamaStack( Datasets, Telemetry, PostTraining, - Memory, + VectorIO, Eval, EvalTasks, Scoring, @@ -69,7 +69,7 @@ class LlamaStack( RESOURCES = [ ("models", Api.models, "register_model", "list_models"), ("shields", Api.shields, "register_shield", "list_shields"), - ("memory_banks", Api.memory_banks, "register_memory_bank", "list_memory_banks"), + ("vector_dbs", Api.vector_dbs, "register_vector_db", "list_vector_dbs"), ("datasets", Api.datasets, "register_dataset", "list_datasets"), ( "scoring_fns", diff --git a/llama_stack/providers/datatypes.py b/llama_stack/providers/datatypes.py index 4815754d2..d0c448f8c 100644 --- a/llama_stack/providers/datatypes.py +++ b/llama_stack/providers/datatypes.py @@ -14,11 +14,11 @@ from llama_stack.apis.datasets import Dataset from llama_stack.apis.datatypes import Api from llama_stack.apis.eval_tasks import EvalTask -from llama_stack.apis.memory_banks.memory_banks import MemoryBank from llama_stack.apis.models import Model from llama_stack.apis.scoring_functions import ScoringFn from llama_stack.apis.shields import Shield from llama_stack.apis.tools import Tool +from llama_stack.apis.vector_dbs import VectorDB class ModelsProtocolPrivate(Protocol): @@ -31,10 +31,10 @@ class ShieldsProtocolPrivate(Protocol): async def register_shield(self, shield: Shield) -> None: ... -class MemoryBanksProtocolPrivate(Protocol): - async def register_memory_bank(self, memory_bank: MemoryBank) -> None: ... +class VectorDBsProtocolPrivate(Protocol): + async def register_vector_db(self, vector_db: VectorDB) -> None: ... - async def unregister_memory_bank(self, memory_bank_id: str) -> None: ... + async def unregister_vector_db(self, vector_db_id: str) -> None: ... class DatasetsProtocolPrivate(Protocol): diff --git a/llama_stack/providers/inline/memory/__init__.py b/llama_stack/providers/inline/vector_io/__init__.py similarity index 100% rename from llama_stack/providers/inline/memory/__init__.py rename to llama_stack/providers/inline/vector_io/__init__.py diff --git a/llama_stack/providers/inline/memory/chroma/__init__.py b/llama_stack/providers/inline/vector_io/chroma/__init__.py similarity index 100% rename from llama_stack/providers/inline/memory/chroma/__init__.py rename to llama_stack/providers/inline/vector_io/chroma/__init__.py diff --git a/llama_stack/providers/inline/memory/chroma/config.py b/llama_stack/providers/inline/vector_io/chroma/config.py similarity index 100% rename from llama_stack/providers/inline/memory/chroma/config.py rename to llama_stack/providers/inline/vector_io/chroma/config.py diff --git a/llama_stack/providers/inline/memory/faiss/__init__.py b/llama_stack/providers/inline/vector_io/faiss/__init__.py similarity index 100% rename from llama_stack/providers/inline/memory/faiss/__init__.py rename to llama_stack/providers/inline/vector_io/faiss/__init__.py diff --git a/llama_stack/providers/inline/memory/faiss/config.py b/llama_stack/providers/inline/vector_io/faiss/config.py similarity index 100% rename from llama_stack/providers/inline/memory/faiss/config.py rename to llama_stack/providers/inline/vector_io/faiss/config.py diff --git a/llama_stack/providers/inline/memory/faiss/faiss.py b/llama_stack/providers/inline/vector_io/faiss/faiss.py similarity index 100% rename from llama_stack/providers/inline/memory/faiss/faiss.py rename to llama_stack/providers/inline/vector_io/faiss/faiss.py diff --git a/llama_stack/providers/registry/memory.py b/llama_stack/providers/registry/vector_io.py similarity index 64% rename from llama_stack/providers/registry/memory.py rename to llama_stack/providers/registry/vector_io.py index 6867a9186..df7b7f4b3 100644 --- a/llama_stack/providers/registry/memory.py +++ b/llama_stack/providers/registry/vector_io.py @@ -38,78 +38,78 @@ EMBEDDING_DEPS = [ def available_providers() -> List[ProviderSpec]: return [ InlineProviderSpec( - api=Api.memory, + api=Api.vector_io, provider_type="inline::meta-reference", pip_packages=EMBEDDING_DEPS + ["faiss-cpu"], - module="llama_stack.providers.inline.memory.faiss", - config_class="llama_stack.providers.inline.memory.faiss.FaissImplConfig", + module="llama_stack.providers.inline.vector_io.faiss", + config_class="llama_stack.providers.inline.vector_io.faiss.FaissImplConfig", deprecation_warning="Please use the `inline::faiss` provider instead.", api_dependencies=[Api.inference], ), InlineProviderSpec( - api=Api.memory, + api=Api.vector_io, provider_type="inline::faiss", pip_packages=EMBEDDING_DEPS + ["faiss-cpu"], - module="llama_stack.providers.inline.memory.faiss", - config_class="llama_stack.providers.inline.memory.faiss.FaissImplConfig", + module="llama_stack.providers.inline.vector_io.faiss", + config_class="llama_stack.providers.inline.vector_io.faiss.FaissImplConfig", api_dependencies=[Api.inference], ), remote_provider_spec( - Api.memory, + Api.vector_io, AdapterSpec( adapter_type="chromadb", pip_packages=EMBEDDING_DEPS + ["chromadb-client"], - module="llama_stack.providers.remote.memory.chroma", - config_class="llama_stack.providers.remote.memory.chroma.ChromaRemoteImplConfig", + module="llama_stack.providers.remote.vector_io.chroma", + config_class="llama_stack.providers.remote.vector_io.chroma.ChromaRemoteImplConfig", ), api_dependencies=[Api.inference], ), InlineProviderSpec( - api=Api.memory, + api=Api.vector_io, provider_type="inline::chromadb", pip_packages=EMBEDDING_DEPS + ["chromadb"], - module="llama_stack.providers.inline.memory.chroma", - config_class="llama_stack.providers.inline.memory.chroma.ChromaInlineImplConfig", + module="llama_stack.providers.inline.vector_io.chroma", + config_class="llama_stack.providers.inline.vector_io.chroma.ChromaInlineImplConfig", api_dependencies=[Api.inference], ), remote_provider_spec( - Api.memory, + Api.vector_io, AdapterSpec( adapter_type="pgvector", pip_packages=EMBEDDING_DEPS + ["psycopg2-binary"], - module="llama_stack.providers.remote.memory.pgvector", - config_class="llama_stack.providers.remote.memory.pgvector.PGVectorConfig", + module="llama_stack.providers.remote.vector_io.pgvector", + config_class="llama_stack.providers.remote.vector_io.pgvector.PGVectorConfig", ), api_dependencies=[Api.inference], ), remote_provider_spec( - Api.memory, + Api.vector_io, AdapterSpec( adapter_type="weaviate", pip_packages=EMBEDDING_DEPS + ["weaviate-client"], - module="llama_stack.providers.remote.memory.weaviate", - config_class="llama_stack.providers.remote.memory.weaviate.WeaviateConfig", - provider_data_validator="llama_stack.providers.remote.memory.weaviate.WeaviateRequestProviderData", + module="llama_stack.providers.remote.vector_io.weaviate", + config_class="llama_stack.providers.remote.vector_io.weaviate.WeaviateConfig", + provider_data_validator="llama_stack.providers.remote.vector_io.weaviate.WeaviateRequestProviderData", ), api_dependencies=[Api.inference], ), remote_provider_spec( - api=Api.memory, + api=Api.vector_io, adapter=AdapterSpec( adapter_type="sample", pip_packages=[], - module="llama_stack.providers.remote.memory.sample", - config_class="llama_stack.providers.remote.memory.sample.SampleConfig", + module="llama_stack.providers.remote.vector_io.sample", + config_class="llama_stack.providers.remote.vector_io.sample.SampleConfig", ), api_dependencies=[], ), remote_provider_spec( - Api.memory, + Api.vector_io, AdapterSpec( adapter_type="qdrant", pip_packages=EMBEDDING_DEPS + ["qdrant-client"], - module="llama_stack.providers.remote.memory.qdrant", - config_class="llama_stack.providers.remote.memory.qdrant.QdrantConfig", + module="llama_stack.providers.remote.vector_io.qdrant", + config_class="llama_stack.providers.remote.vector_io.qdrant.QdrantConfig", ), api_dependencies=[Api.inference], ), diff --git a/llama_stack/providers/remote/memory/__init__.py b/llama_stack/providers/remote/vector_io/__init__.py similarity index 100% rename from llama_stack/providers/remote/memory/__init__.py rename to llama_stack/providers/remote/vector_io/__init__.py diff --git a/llama_stack/providers/remote/memory/chroma/__init__.py b/llama_stack/providers/remote/vector_io/chroma/__init__.py similarity index 100% rename from llama_stack/providers/remote/memory/chroma/__init__.py rename to llama_stack/providers/remote/vector_io/chroma/__init__.py diff --git a/llama_stack/providers/remote/memory/chroma/chroma.py b/llama_stack/providers/remote/vector_io/chroma/chroma.py similarity index 100% rename from llama_stack/providers/remote/memory/chroma/chroma.py rename to llama_stack/providers/remote/vector_io/chroma/chroma.py diff --git a/llama_stack/providers/remote/memory/chroma/config.py b/llama_stack/providers/remote/vector_io/chroma/config.py similarity index 100% rename from llama_stack/providers/remote/memory/chroma/config.py rename to llama_stack/providers/remote/vector_io/chroma/config.py diff --git a/llama_stack/providers/remote/memory/pgvector/__init__.py b/llama_stack/providers/remote/vector_io/pgvector/__init__.py similarity index 100% rename from llama_stack/providers/remote/memory/pgvector/__init__.py rename to llama_stack/providers/remote/vector_io/pgvector/__init__.py diff --git a/llama_stack/providers/remote/memory/pgvector/config.py b/llama_stack/providers/remote/vector_io/pgvector/config.py similarity index 100% rename from llama_stack/providers/remote/memory/pgvector/config.py rename to llama_stack/providers/remote/vector_io/pgvector/config.py diff --git a/llama_stack/providers/remote/memory/pgvector/pgvector.py b/llama_stack/providers/remote/vector_io/pgvector/pgvector.py similarity index 100% rename from llama_stack/providers/remote/memory/pgvector/pgvector.py rename to llama_stack/providers/remote/vector_io/pgvector/pgvector.py diff --git a/llama_stack/providers/remote/memory/qdrant/__init__.py b/llama_stack/providers/remote/vector_io/qdrant/__init__.py similarity index 100% rename from llama_stack/providers/remote/memory/qdrant/__init__.py rename to llama_stack/providers/remote/vector_io/qdrant/__init__.py diff --git a/llama_stack/providers/remote/memory/qdrant/config.py b/llama_stack/providers/remote/vector_io/qdrant/config.py similarity index 100% rename from llama_stack/providers/remote/memory/qdrant/config.py rename to llama_stack/providers/remote/vector_io/qdrant/config.py diff --git a/llama_stack/providers/remote/memory/qdrant/qdrant.py b/llama_stack/providers/remote/vector_io/qdrant/qdrant.py similarity index 100% rename from llama_stack/providers/remote/memory/qdrant/qdrant.py rename to llama_stack/providers/remote/vector_io/qdrant/qdrant.py diff --git a/llama_stack/providers/remote/memory/sample/__init__.py b/llama_stack/providers/remote/vector_io/sample/__init__.py similarity index 100% rename from llama_stack/providers/remote/memory/sample/__init__.py rename to llama_stack/providers/remote/vector_io/sample/__init__.py diff --git a/llama_stack/providers/remote/memory/sample/config.py b/llama_stack/providers/remote/vector_io/sample/config.py similarity index 100% rename from llama_stack/providers/remote/memory/sample/config.py rename to llama_stack/providers/remote/vector_io/sample/config.py diff --git a/llama_stack/providers/remote/memory/sample/sample.py b/llama_stack/providers/remote/vector_io/sample/sample.py similarity index 100% rename from llama_stack/providers/remote/memory/sample/sample.py rename to llama_stack/providers/remote/vector_io/sample/sample.py diff --git a/llama_stack/providers/remote/memory/weaviate/__init__.py b/llama_stack/providers/remote/vector_io/weaviate/__init__.py similarity index 100% rename from llama_stack/providers/remote/memory/weaviate/__init__.py rename to llama_stack/providers/remote/vector_io/weaviate/__init__.py diff --git a/llama_stack/providers/remote/memory/weaviate/config.py b/llama_stack/providers/remote/vector_io/weaviate/config.py similarity index 100% rename from llama_stack/providers/remote/memory/weaviate/config.py rename to llama_stack/providers/remote/vector_io/weaviate/config.py diff --git a/llama_stack/providers/remote/memory/weaviate/weaviate.py b/llama_stack/providers/remote/vector_io/weaviate/weaviate.py similarity index 100% rename from llama_stack/providers/remote/memory/weaviate/weaviate.py rename to llama_stack/providers/remote/vector_io/weaviate/weaviate.py From 78a481bb22b4181a699a82987f82633f27affec5 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 10:02:15 -0800 Subject: [PATCH 070/107] [memory refactor][2/n] Update faiss and make it pass tests (#830) See https://github.com/meta-llama/llama-stack/issues/827 for the broader design. Second part: - updates routing table / router code - updates the faiss implementation ## Test Plan ``` pytest -s -v -k sentence test_vector_io.py --env EMBEDDING_DIMENSION=384 ``` --- llama_stack/distribution/routers/__init__.py | 8 +- llama_stack/distribution/routers/routers.py | 45 ++-- .../inline/vector_io/faiss/__init__.py | 4 +- .../providers/inline/vector_io/faiss/faiss.py | 107 ++++------ llama_stack/providers/registry/agents.py | 4 +- .../providers/registry/tool_runtime.py | 2 +- llama_stack/providers/tests/conftest.py | 2 +- .../providers/tests/memory/test_memory.py | 192 ----------------- llama_stack/providers/tests/resolver.py | 6 +- .../tests/{memory => vector_io}/__init__.py | 0 .../tests/{memory => vector_io}/conftest.py | 24 +-- .../tests/{memory => vector_io}/fixtures.py | 31 +-- .../{memory => vector_io}/fixtures/dummy.pdf | Bin .../tests/vector_io/test_vector_io.py | 200 ++++++++++++++++++ .../test_vector_store.py | 7 +- .../providers/utils/memory/vector_store.py | 63 +++--- tests/client-sdk/conftest.py | 1 + .../{memory => vector_io}/__init__.py | 0 .../test_vector_io.py} | 0 19 files changed, 343 insertions(+), 353 deletions(-) delete mode 100644 llama_stack/providers/tests/memory/test_memory.py rename llama_stack/providers/tests/{memory => vector_io}/__init__.py (100%) rename llama_stack/providers/tests/{memory => vector_io}/conftest.py (79%) rename llama_stack/providers/tests/{memory => vector_io}/fixtures.py (80%) rename llama_stack/providers/tests/{memory => vector_io}/fixtures/dummy.pdf (100%) create mode 100644 llama_stack/providers/tests/vector_io/test_vector_io.py rename llama_stack/providers/tests/{memory => vector_io}/test_vector_store.py (94%) rename tests/client-sdk/{memory => vector_io}/__init__.py (100%) rename tests/client-sdk/{memory/test_memory.py => vector_io/test_vector_io.py} (100%) diff --git a/llama_stack/distribution/routers/__init__.py b/llama_stack/distribution/routers/__init__.py index f19a2bffc..156cda385 100644 --- a/llama_stack/distribution/routers/__init__.py +++ b/llama_stack/distribution/routers/__init__.py @@ -14,11 +14,11 @@ from llama_stack.providers.datatypes import Api, RoutingTable from .routing_tables import ( DatasetsRoutingTable, EvalTasksRoutingTable, - MemoryBanksRoutingTable, ModelsRoutingTable, ScoringFunctionsRoutingTable, ShieldsRoutingTable, ToolGroupsRoutingTable, + VectorDBsRoutingTable, ) @@ -29,7 +29,7 @@ async def get_routing_table_impl( dist_registry: DistributionRegistry, ) -> Any: api_to_tables = { - "memory_banks": MemoryBanksRoutingTable, + "vector_dbs": VectorDBsRoutingTable, "models": ModelsRoutingTable, "shields": ShieldsRoutingTable, "datasets": DatasetsRoutingTable, @@ -51,14 +51,14 @@ async def get_auto_router_impl(api: Api, routing_table: RoutingTable, _deps) -> DatasetIORouter, EvalRouter, InferenceRouter, - MemoryRouter, SafetyRouter, ScoringRouter, ToolRuntimeRouter, + VectorIORouter, ) api_to_routers = { - "memory": MemoryRouter, + "vector_io": VectorIORouter, "inference": InferenceRouter, "safety": SafetyRouter, "datasetio": DatasetIORouter, diff --git a/llama_stack/distribution/routers/routers.py b/llama_stack/distribution/routers/routers.py index 8080b9dff..979c68b72 100644 --- a/llama_stack/distribution/routers/routers.py +++ b/llama_stack/distribution/routers/routers.py @@ -27,8 +27,6 @@ from llama_stack.apis.inference import ( ToolDefinition, ToolPromptFormat, ) -from llama_stack.apis.memory import Memory, MemoryBankDocument, QueryDocumentsResponse -from llama_stack.apis.memory_banks.memory_banks import BankParams from llama_stack.apis.models import ModelType from llama_stack.apis.safety import RunShieldResponse, Safety from llama_stack.apis.scoring import ( @@ -39,11 +37,12 @@ from llama_stack.apis.scoring import ( ) from llama_stack.apis.shields import Shield from llama_stack.apis.tools import ToolDef, ToolRuntime +from llama_stack.apis.vector_io import Chunk, QueryChunksResponse, VectorIO from llama_stack.providers.datatypes import RoutingTable -class MemoryRouter(Memory): - """Routes to an provider based on the memory bank identifier""" +class VectorIORouter(VectorIO): + """Routes to an provider based on the vector db identifier""" def __init__( self, @@ -57,38 +56,40 @@ class MemoryRouter(Memory): async def shutdown(self) -> None: pass - async def register_memory_bank( + async def register_vector_db( self, - memory_bank_id: str, - params: BankParams, + vector_db_id: str, + embedding_model: str, + embedding_dimension: Optional[int] = 384, provider_id: Optional[str] = None, - provider_memorybank_id: Optional[str] = None, + provider_vector_db_id: Optional[str] = None, ) -> None: - await self.routing_table.register_memory_bank( - memory_bank_id, - params, + await self.routing_table.register_vector_db( + vector_db_id, + embedding_model, + embedding_dimension, provider_id, - provider_memorybank_id, + provider_vector_db_id, ) - async def insert_documents( + async def insert_chunks( self, - bank_id: str, - documents: List[MemoryBankDocument], + vector_db_id: str, + chunks: List[Chunk], ttl_seconds: Optional[int] = None, ) -> None: - return await self.routing_table.get_provider_impl(bank_id).insert_documents( - bank_id, documents, ttl_seconds + return await self.routing_table.get_provider_impl(vector_db_id).insert_chunks( + vector_db_id, chunks, ttl_seconds ) - async def query_documents( + async def query_chunks( self, - bank_id: str, + vector_db_id: str, query: InterleavedContent, params: Optional[Dict[str, Any]] = None, - ) -> QueryDocumentsResponse: - return await self.routing_table.get_provider_impl(bank_id).query_documents( - bank_id, query, params + ) -> QueryChunksResponse: + return await self.routing_table.get_provider_impl(vector_db_id).query_chunks( + vector_db_id, query, params ) diff --git a/llama_stack/providers/inline/vector_io/faiss/__init__.py b/llama_stack/providers/inline/vector_io/faiss/__init__.py index 2d7ede3b1..32cf262fd 100644 --- a/llama_stack/providers/inline/vector_io/faiss/__init__.py +++ b/llama_stack/providers/inline/vector_io/faiss/__init__.py @@ -11,12 +11,12 @@ from .config import FaissImplConfig async def get_provider_impl(config: FaissImplConfig, deps: Dict[Api, ProviderSpec]): - from .faiss import FaissMemoryImpl + from .faiss import FaissVectorIOImpl assert isinstance( config, FaissImplConfig ), f"Unexpected config type: {type(config)}" - impl = FaissMemoryImpl(config, deps[Api.inference]) + impl = FaissVectorIOImpl(config, deps[Api.inference]) await impl.initialize() return impl diff --git a/llama_stack/providers/inline/vector_io/faiss/faiss.py b/llama_stack/providers/inline/vector_io/faiss/faiss.py index af398801a..db53302bb 100644 --- a/llama_stack/providers/inline/vector_io/faiss/faiss.py +++ b/llama_stack/providers/inline/vector_io/faiss/faiss.py @@ -17,35 +17,28 @@ import numpy as np from numpy.typing import NDArray from llama_stack.apis.inference import InterleavedContent -from llama_stack.apis.memory import ( - Chunk, - Memory, - MemoryBankDocument, - QueryDocumentsResponse, -) -from llama_stack.apis.memory_banks import MemoryBank, MemoryBankType, VectorMemoryBank -from llama_stack.providers.datatypes import Api, MemoryBanksProtocolPrivate +from llama_stack.apis.vector_dbs import VectorDB +from llama_stack.apis.vector_io import Chunk, QueryChunksResponse, VectorIO +from llama_stack.providers.datatypes import Api, VectorDBsProtocolPrivate from llama_stack.providers.utils.kvstore import kvstore_impl from llama_stack.providers.utils.memory.vector_store import ( - BankWithIndex, EmbeddingIndex, + VectorDBWithIndex, ) from .config import FaissImplConfig logger = logging.getLogger(__name__) -MEMORY_BANKS_PREFIX = "memory_banks:v2::" +VECTOR_DBS_PREFIX = "vector_dbs:v2::" FAISS_INDEX_PREFIX = "faiss_index:v2::" class FaissIndex(EmbeddingIndex): - id_by_index: Dict[int, str] chunk_by_index: Dict[int, str] def __init__(self, dimension: int, kvstore=None, bank_id: str = None): self.index = faiss.IndexFlatL2(dimension) - self.id_by_index = {} self.chunk_by_index = {} self.kvstore = kvstore self.bank_id = bank_id @@ -65,7 +58,6 @@ class FaissIndex(EmbeddingIndex): if stored_data: data = json.loads(stored_data) - self.id_by_index = {int(k): v for k, v in data["id_by_index"].items()} self.chunk_by_index = { int(k): Chunk.model_validate_json(v) for k, v in data["chunk_by_index"].items() @@ -82,7 +74,6 @@ class FaissIndex(EmbeddingIndex): buffer = io.BytesIO() np.savetxt(buffer, np_index) data = { - "id_by_index": self.id_by_index, "chunk_by_index": { k: v.model_dump_json() for k, v in self.chunk_by_index.items() }, @@ -108,10 +99,9 @@ class FaissIndex(EmbeddingIndex): f"Embedding dimension mismatch. Expected {self.index.d}, got {embedding_dim}" ) - indexlen = len(self.id_by_index) + indexlen = len(self.chunk_by_index) for i, chunk in enumerate(chunks): self.chunk_by_index[indexlen + i] = chunk - self.id_by_index[indexlen + i] = chunk.document_id self.index.add(np.array(embeddings).astype(np.float32)) @@ -120,7 +110,7 @@ class FaissIndex(EmbeddingIndex): async def query( self, embedding: NDArray, k: int, score_threshold: float - ) -> QueryDocumentsResponse: + ) -> QueryChunksResponse: distances, indices = self.index.search( embedding.reshape(1, -1).astype(np.float32), k ) @@ -133,10 +123,10 @@ class FaissIndex(EmbeddingIndex): chunks.append(self.chunk_by_index[int(i)]) scores.append(1.0 / float(d)) - return QueryDocumentsResponse(chunks=chunks, scores=scores) + return QueryChunksResponse(chunks=chunks, scores=scores) -class FaissMemoryImpl(Memory, MemoryBanksProtocolPrivate): +class FaissVectorIOImpl(VectorIO, VectorDBsProtocolPrivate): def __init__(self, config: FaissImplConfig, inference_api: Api.inference) -> None: self.config = config self.inference_api = inference_api @@ -146,77 +136,74 @@ class FaissMemoryImpl(Memory, MemoryBanksProtocolPrivate): async def initialize(self) -> None: self.kvstore = await kvstore_impl(self.config.kvstore) # Load existing banks from kvstore - start_key = MEMORY_BANKS_PREFIX - end_key = f"{MEMORY_BANKS_PREFIX}\xff" - stored_banks = await self.kvstore.range(start_key, end_key) + start_key = VECTOR_DBS_PREFIX + end_key = f"{VECTOR_DBS_PREFIX}\xff" + stored_vector_dbs = await self.kvstore.range(start_key, end_key) - for bank_data in stored_banks: - bank = VectorMemoryBank.model_validate_json(bank_data) - index = BankWithIndex( - bank, + for vector_db_data in stored_vector_dbs: + vector_db = VectorDB.model_validate_json(vector_db_data) + index = VectorDBWithIndex( + vector_db, await FaissIndex.create( - bank.embedding_dimension, self.kvstore, bank.identifier + vector_db.embedding_dimension, self.kvstore, vector_db.identifier ), self.inference_api, ) - self.cache[bank.identifier] = index + self.cache[vector_db.identifier] = index async def shutdown(self) -> None: # Cleanup if needed pass - async def register_memory_bank( + async def register_vector_db( self, - memory_bank: MemoryBank, + vector_db: VectorDB, ) -> None: - assert ( - memory_bank.memory_bank_type == MemoryBankType.vector.value - ), f"Only vector banks are supported {memory_bank.type}" - - # Store in kvstore - key = f"{MEMORY_BANKS_PREFIX}{memory_bank.identifier}" + key = f"{VECTOR_DBS_PREFIX}{vector_db.identifier}" await self.kvstore.set( key=key, - value=memory_bank.model_dump_json(), + value=vector_db.model_dump_json(), ) # Store in cache - self.cache[memory_bank.identifier] = BankWithIndex( - memory_bank, - await FaissIndex.create( - memory_bank.embedding_dimension, self.kvstore, memory_bank.identifier + self.cache[vector_db.identifier] = VectorDBWithIndex( + vector_db=vector_db, + index=await FaissIndex.create( + vector_db.embedding_dimension, self.kvstore, vector_db.identifier ), - self.inference_api, + inference_api=self.inference_api, ) - async def list_memory_banks(self) -> List[MemoryBank]: - return [i.bank for i in self.cache.values()] + async def list_vector_dbs(self) -> List[VectorDB]: + return [i.vector_db for i in self.cache.values()] - async def unregister_memory_bank(self, memory_bank_id: str) -> None: - await self.cache[memory_bank_id].index.delete() - del self.cache[memory_bank_id] - await self.kvstore.delete(f"{MEMORY_BANKS_PREFIX}{memory_bank_id}") + async def unregister_vector_db(self, vector_db_id: str) -> None: + await self.cache[vector_db_id].index.delete() + del self.cache[vector_db_id] + await self.kvstore.delete(f"{VECTOR_DBS_PREFIX}{vector_db_id}") - async def insert_documents( + async def insert_chunks( self, - bank_id: str, - documents: List[MemoryBankDocument], + vector_db_id: str, + chunks: List[Chunk], ttl_seconds: Optional[int] = None, ) -> None: - index = self.cache.get(bank_id) + index = self.cache.get(vector_db_id) if index is None: - raise ValueError(f"Bank {bank_id} not found. found: {self.cache.keys()}") + raise ValueError( + f"Vector DB {vector_db_id} not found. found: {self.cache.keys()}" + ) - await index.insert_documents(documents) + await index.insert_chunks(chunks) - async def query_documents( + async def query_chunks( self, - bank_id: str, + vector_db_id: str, query: InterleavedContent, params: Optional[Dict[str, Any]] = None, - ) -> QueryDocumentsResponse: - index = self.cache.get(bank_id) + ) -> QueryChunksResponse: + index = self.cache.get(vector_db_id) if index is None: - raise ValueError(f"Bank {bank_id} not found") + raise ValueError(f"Vector DB {vector_db_id} not found") - return await index.query_documents(query, params) + return await index.query_chunks(query, params) diff --git a/llama_stack/providers/registry/agents.py b/llama_stack/providers/registry/agents.py index 3e38b1adc..655303f98 100644 --- a/llama_stack/providers/registry/agents.py +++ b/llama_stack/providers/registry/agents.py @@ -33,8 +33,8 @@ def available_providers() -> List[ProviderSpec]: api_dependencies=[ Api.inference, Api.safety, - Api.memory, - Api.memory_banks, + Api.vector_io, + Api.vector_dbs, Api.tool_runtime, Api.tool_groups, ], diff --git a/llama_stack/providers/registry/tool_runtime.py b/llama_stack/providers/registry/tool_runtime.py index 40299edad..b3ea68949 100644 --- a/llama_stack/providers/registry/tool_runtime.py +++ b/llama_stack/providers/registry/tool_runtime.py @@ -23,7 +23,7 @@ def available_providers() -> List[ProviderSpec]: pip_packages=[], module="llama_stack.providers.inline.tool_runtime.memory", config_class="llama_stack.providers.inline.tool_runtime.memory.config.MemoryToolRuntimeConfig", - api_dependencies=[Api.memory, Api.memory_banks, Api.inference], + api_dependencies=[Api.vector_io, Api.vector_dbs, Api.inference], ), InlineProviderSpec( api=Api.tool_runtime, diff --git a/llama_stack/providers/tests/conftest.py b/llama_stack/providers/tests/conftest.py index 4aa53a687..7d0d2ae74 100644 --- a/llama_stack/providers/tests/conftest.py +++ b/llama_stack/providers/tests/conftest.py @@ -302,7 +302,7 @@ def pytest_collection_modifyitems(session, config, items): pytest_plugins = [ "llama_stack.providers.tests.inference.fixtures", "llama_stack.providers.tests.safety.fixtures", - "llama_stack.providers.tests.memory.fixtures", + "llama_stack.providers.tests.vector_io.fixtures", "llama_stack.providers.tests.agents.fixtures", "llama_stack.providers.tests.datasetio.fixtures", "llama_stack.providers.tests.scoring.fixtures", diff --git a/llama_stack/providers/tests/memory/test_memory.py b/llama_stack/providers/tests/memory/test_memory.py deleted file mode 100644 index 801b04dfc..000000000 --- a/llama_stack/providers/tests/memory/test_memory.py +++ /dev/null @@ -1,192 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the terms described in the LICENSE file in -# the root directory of this source tree. - -import uuid - -import pytest - -from llama_stack.apis.memory import MemoryBankDocument, QueryDocumentsResponse - -from llama_stack.apis.memory_banks import ( - MemoryBank, - MemoryBanks, - VectorMemoryBankParams, -) - -# How to run this test: -# -# pytest llama_stack/providers/tests/memory/test_memory.py -# -m "sentence_transformers" --env EMBEDDING_DIMENSION=384 -# -v -s --tb=short --disable-warnings - - -@pytest.fixture -def sample_documents(): - return [ - MemoryBankDocument( - document_id="doc1", - content="Python is a high-level programming language.", - metadata={"category": "programming", "difficulty": "beginner"}, - ), - MemoryBankDocument( - document_id="doc2", - content="Machine learning is a subset of artificial intelligence.", - metadata={"category": "AI", "difficulty": "advanced"}, - ), - MemoryBankDocument( - document_id="doc3", - content="Data structures are fundamental to computer science.", - metadata={"category": "computer science", "difficulty": "intermediate"}, - ), - MemoryBankDocument( - document_id="doc4", - content="Neural networks are inspired by biological neural networks.", - metadata={"category": "AI", "difficulty": "advanced"}, - ), - ] - - -async def register_memory_bank( - banks_impl: MemoryBanks, embedding_model: str -) -> MemoryBank: - bank_id = f"test_bank_{uuid.uuid4().hex}" - return await banks_impl.register_memory_bank( - memory_bank_id=bank_id, - params=VectorMemoryBankParams( - embedding_model=embedding_model, - chunk_size_in_tokens=512, - overlap_size_in_tokens=64, - ), - ) - - -class TestMemory: - @pytest.mark.asyncio - async def test_banks_list(self, memory_stack, embedding_model): - _, banks_impl = memory_stack - - # Register a test bank - registered_bank = await register_memory_bank(banks_impl, embedding_model) - - try: - # Verify our bank shows up in list - response = await banks_impl.list_memory_banks() - assert isinstance(response, list) - assert any( - bank.memory_bank_id == registered_bank.memory_bank_id - for bank in response - ) - finally: - # Clean up - await banks_impl.unregister_memory_bank(registered_bank.memory_bank_id) - - # Verify our bank was removed - response = await banks_impl.list_memory_banks() - assert all( - bank.memory_bank_id != registered_bank.memory_bank_id for bank in response - ) - - @pytest.mark.asyncio - async def test_banks_register(self, memory_stack, embedding_model): - _, banks_impl = memory_stack - - bank_id = f"test_bank_{uuid.uuid4().hex}" - - try: - # Register initial bank - await banks_impl.register_memory_bank( - memory_bank_id=bank_id, - params=VectorMemoryBankParams( - embedding_model=embedding_model, - chunk_size_in_tokens=512, - overlap_size_in_tokens=64, - ), - ) - - # Verify our bank exists - response = await banks_impl.list_memory_banks() - assert isinstance(response, list) - assert any(bank.memory_bank_id == bank_id for bank in response) - - # Try registering same bank again - await banks_impl.register_memory_bank( - memory_bank_id=bank_id, - params=VectorMemoryBankParams( - embedding_model=embedding_model, - chunk_size_in_tokens=512, - overlap_size_in_tokens=64, - ), - ) - - # Verify still only one instance of our bank - response = await banks_impl.list_memory_banks() - assert isinstance(response, list) - assert ( - len([bank for bank in response if bank.memory_bank_id == bank_id]) == 1 - ) - finally: - # Clean up - await banks_impl.unregister_memory_bank(bank_id) - - @pytest.mark.asyncio - async def test_query_documents( - self, memory_stack, embedding_model, sample_documents - ): - memory_impl, banks_impl = memory_stack - - with pytest.raises(ValueError): - await memory_impl.insert_documents("test_bank", sample_documents) - - registered_bank = await register_memory_bank(banks_impl, embedding_model) - await memory_impl.insert_documents( - registered_bank.memory_bank_id, sample_documents - ) - - query1 = "programming language" - response1 = await memory_impl.query_documents( - registered_bank.memory_bank_id, query1 - ) - assert_valid_response(response1) - assert any("Python" in chunk.content for chunk in response1.chunks) - - # Test case 3: Query with semantic similarity - query3 = "AI and brain-inspired computing" - response3 = await memory_impl.query_documents( - registered_bank.memory_bank_id, query3 - ) - assert_valid_response(response3) - assert any( - "neural networks" in chunk.content.lower() for chunk in response3.chunks - ) - - # Test case 4: Query with limit on number of results - query4 = "computer" - params4 = {"max_chunks": 2} - response4 = await memory_impl.query_documents( - registered_bank.memory_bank_id, query4, params4 - ) - assert_valid_response(response4) - assert len(response4.chunks) <= 2 - - # Test case 5: Query with threshold on similarity score - query5 = "quantum computing" # Not directly related to any document - params5 = {"score_threshold": 0.01} - response5 = await memory_impl.query_documents( - registered_bank.memory_bank_id, query5, params5 - ) - assert_valid_response(response5) - print("The scores are:", response5.scores) - assert all(score >= 0.01 for score in response5.scores) - - -def assert_valid_response(response: QueryDocumentsResponse): - assert isinstance(response, QueryDocumentsResponse) - assert len(response.chunks) > 0 - assert len(response.scores) > 0 - assert len(response.chunks) == len(response.scores) - for chunk in response.chunks: - assert isinstance(chunk.content, str) - assert chunk.document_id is not None diff --git a/llama_stack/providers/tests/resolver.py b/llama_stack/providers/tests/resolver.py index 81816d51e..f0c4c530e 100644 --- a/llama_stack/providers/tests/resolver.py +++ b/llama_stack/providers/tests/resolver.py @@ -12,11 +12,11 @@ from pydantic import BaseModel from llama_stack.apis.datasets import DatasetInput from llama_stack.apis.eval_tasks import EvalTaskInput -from llama_stack.apis.memory_banks import MemoryBankInput from llama_stack.apis.models import ModelInput from llama_stack.apis.scoring_functions import ScoringFnInput from llama_stack.apis.shields import ShieldInput from llama_stack.apis.tools import ToolGroupInput +from llama_stack.apis.vector_dbs import VectorDBInput from llama_stack.distribution.build import print_pip_install_help from llama_stack.distribution.configure import parse_and_maybe_upgrade_config from llama_stack.distribution.datatypes import Provider, StackRunConfig @@ -39,7 +39,7 @@ async def construct_stack_for_test( provider_data: Optional[Dict[str, Any]] = None, models: Optional[List[ModelInput]] = None, shields: Optional[List[ShieldInput]] = None, - memory_banks: Optional[List[MemoryBankInput]] = None, + vector_dbs: Optional[List[VectorDBInput]] = None, datasets: Optional[List[DatasetInput]] = None, scoring_fns: Optional[List[ScoringFnInput]] = None, eval_tasks: Optional[List[EvalTaskInput]] = None, @@ -53,7 +53,7 @@ async def construct_stack_for_test( metadata_store=SqliteKVStoreConfig(db_path=sqlite_file.name), models=models or [], shields=shields or [], - memory_banks=memory_banks or [], + vector_dbs=vector_dbs or [], datasets=datasets or [], scoring_fns=scoring_fns or [], eval_tasks=eval_tasks or [], diff --git a/llama_stack/providers/tests/memory/__init__.py b/llama_stack/providers/tests/vector_io/__init__.py similarity index 100% rename from llama_stack/providers/tests/memory/__init__.py rename to llama_stack/providers/tests/vector_io/__init__.py diff --git a/llama_stack/providers/tests/memory/conftest.py b/llama_stack/providers/tests/vector_io/conftest.py similarity index 79% rename from llama_stack/providers/tests/memory/conftest.py rename to llama_stack/providers/tests/vector_io/conftest.py index 87dec4beb..df5c8ea6a 100644 --- a/llama_stack/providers/tests/memory/conftest.py +++ b/llama_stack/providers/tests/vector_io/conftest.py @@ -13,14 +13,14 @@ from ..conftest import ( ) from ..inference.fixtures import INFERENCE_FIXTURES -from .fixtures import MEMORY_FIXTURES +from .fixtures import VECTOR_IO_FIXTURES DEFAULT_PROVIDER_COMBINATIONS = [ pytest.param( { "inference": "sentence_transformers", - "memory": "faiss", + "vector_io": "faiss", }, id="sentence_transformers", marks=pytest.mark.sentence_transformers, @@ -28,7 +28,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ pytest.param( { "inference": "ollama", - "memory": "faiss", + "vector_io": "faiss", }, id="ollama", marks=pytest.mark.ollama, @@ -36,7 +36,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ pytest.param( { "inference": "sentence_transformers", - "memory": "chroma", + "vector_io": "chroma", }, id="chroma", marks=pytest.mark.chroma, @@ -44,7 +44,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ pytest.param( { "inference": "bedrock", - "memory": "qdrant", + "vector_io": "qdrant", }, id="qdrant", marks=pytest.mark.qdrant, @@ -52,7 +52,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ pytest.param( { "inference": "fireworks", - "memory": "weaviate", + "vector_io": "weaviate", }, id="weaviate", marks=pytest.mark.weaviate, @@ -61,7 +61,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ def pytest_configure(config): - for fixture_name in MEMORY_FIXTURES: + for fixture_name in VECTOR_IO_FIXTURES: config.addinivalue_line( "markers", f"{fixture_name}: marks tests as {fixture_name} specific", @@ -69,7 +69,7 @@ def pytest_configure(config): def pytest_generate_tests(metafunc): - test_config = get_test_config_for_api(metafunc.config, "memory") + test_config = get_test_config_for_api(metafunc.config, "vector_io") if "embedding_model" in metafunc.fixturenames: model = getattr(test_config, "embedding_model", None) # Fall back to the default if not specified by the config file @@ -81,16 +81,16 @@ def pytest_generate_tests(metafunc): metafunc.parametrize("embedding_model", params, indirect=True) - if "memory_stack" in metafunc.fixturenames: + if "vector_io_stack" in metafunc.fixturenames: available_fixtures = { "inference": INFERENCE_FIXTURES, - "memory": MEMORY_FIXTURES, + "vector_io": VECTOR_IO_FIXTURES, } combinations = ( get_provider_fixture_overrides_from_test_config( - metafunc.config, "memory", DEFAULT_PROVIDER_COMBINATIONS + metafunc.config, "vector_io", DEFAULT_PROVIDER_COMBINATIONS ) or get_provider_fixture_overrides(metafunc.config, available_fixtures) or DEFAULT_PROVIDER_COMBINATIONS ) - metafunc.parametrize("memory_stack", combinations, indirect=True) + metafunc.parametrize("vector_io_stack", combinations, indirect=True) diff --git a/llama_stack/providers/tests/memory/fixtures.py b/llama_stack/providers/tests/vector_io/fixtures.py similarity index 80% rename from llama_stack/providers/tests/memory/fixtures.py rename to llama_stack/providers/tests/vector_io/fixtures.py index b9dbb84f7..c8d5fa8cf 100644 --- a/llama_stack/providers/tests/memory/fixtures.py +++ b/llama_stack/providers/tests/vector_io/fixtures.py @@ -12,11 +12,12 @@ import pytest_asyncio from llama_stack.apis.models import ModelInput, ModelType from llama_stack.distribution.datatypes import Api, Provider -from llama_stack.providers.inline.memory.chroma import ChromaInlineImplConfig -from llama_stack.providers.inline.memory.faiss import FaissImplConfig -from llama_stack.providers.remote.memory.chroma import ChromaRemoteImplConfig -from llama_stack.providers.remote.memory.pgvector import PGVectorConfig -from llama_stack.providers.remote.memory.weaviate import WeaviateConfig + +from llama_stack.providers.inline.vector_io.chroma import ChromaInlineImplConfig +from llama_stack.providers.inline.vector_io.faiss import FaissImplConfig +from llama_stack.providers.remote.vector_io.chroma import ChromaRemoteImplConfig +from llama_stack.providers.remote.vector_io.pgvector import PGVectorConfig +from llama_stack.providers.remote.vector_io.weaviate import WeaviateConfig from llama_stack.providers.tests.resolver import construct_stack_for_test from llama_stack.providers.utils.kvstore.config import SqliteKVStoreConfig @@ -32,12 +33,12 @@ def embedding_model(request): @pytest.fixture(scope="session") -def memory_remote() -> ProviderFixture: +def vector_io_remote() -> ProviderFixture: return remote_stack_fixture() @pytest.fixture(scope="session") -def memory_faiss() -> ProviderFixture: +def vector_io_faiss() -> ProviderFixture: temp_file = tempfile.NamedTemporaryFile(delete=False, suffix=".db") return ProviderFixture( providers=[ @@ -53,7 +54,7 @@ def memory_faiss() -> ProviderFixture: @pytest.fixture(scope="session") -def memory_pgvector() -> ProviderFixture: +def vector_io_pgvector() -> ProviderFixture: return ProviderFixture( providers=[ Provider( @@ -72,7 +73,7 @@ def memory_pgvector() -> ProviderFixture: @pytest.fixture(scope="session") -def memory_weaviate() -> ProviderFixture: +def vector_io_weaviate() -> ProviderFixture: return ProviderFixture( providers=[ Provider( @@ -89,7 +90,7 @@ def memory_weaviate() -> ProviderFixture: @pytest.fixture(scope="session") -def memory_chroma() -> ProviderFixture: +def vector_io_chroma() -> ProviderFixture: url = os.getenv("CHROMA_URL") if url: config = ChromaRemoteImplConfig(url=url) @@ -110,23 +111,23 @@ def memory_chroma() -> ProviderFixture: ) -MEMORY_FIXTURES = ["faiss", "pgvector", "weaviate", "remote", "chroma"] +VECTOR_IO_FIXTURES = ["faiss", "pgvector", "weaviate", "chroma"] @pytest_asyncio.fixture(scope="session") -async def memory_stack(embedding_model, request): +async def vector_io_stack(embedding_model, request): fixture_dict = request.param providers = {} provider_data = {} - for key in ["inference", "memory"]: + for key in ["inference", "vector_io"]: fixture = request.getfixturevalue(f"{key}_{fixture_dict[key]}") providers[key] = fixture.providers if fixture.provider_data: provider_data.update(fixture.provider_data) test_stack = await construct_stack_for_test( - [Api.memory, Api.inference], + [Api.vector_io, Api.inference], providers, provider_data, models=[ @@ -140,4 +141,4 @@ async def memory_stack(embedding_model, request): ], ) - return test_stack.impls[Api.memory], test_stack.impls[Api.memory_banks] + return test_stack.impls[Api.vector_io], test_stack.impls[Api.vector_dbs] diff --git a/llama_stack/providers/tests/memory/fixtures/dummy.pdf b/llama_stack/providers/tests/vector_io/fixtures/dummy.pdf similarity index 100% rename from llama_stack/providers/tests/memory/fixtures/dummy.pdf rename to llama_stack/providers/tests/vector_io/fixtures/dummy.pdf diff --git a/llama_stack/providers/tests/vector_io/test_vector_io.py b/llama_stack/providers/tests/vector_io/test_vector_io.py new file mode 100644 index 000000000..901b8bd11 --- /dev/null +++ b/llama_stack/providers/tests/vector_io/test_vector_io.py @@ -0,0 +1,200 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +import uuid + +import pytest + +from llama_stack.apis.vector_dbs import ListVectorDBsResponse, VectorDB +from llama_stack.apis.vector_io import QueryChunksResponse + +from llama_stack.providers.utils.memory.vector_store import ( + make_overlapped_chunks, + MemoryBankDocument, +) + +# How to run this test: +# +# pytest llama_stack/providers/tests/memory/test_memory.py +# -m "sentence_transformers" --env EMBEDDING_DIMENSION=384 +# -v -s --tb=short --disable-warnings + + +@pytest.fixture(scope="session") +def sample_chunks(): + docs = [ + MemoryBankDocument( + document_id="doc1", + content="Python is a high-level programming language.", + metadata={"category": "programming", "difficulty": "beginner"}, + ), + MemoryBankDocument( + document_id="doc2", + content="Machine learning is a subset of artificial intelligence.", + metadata={"category": "AI", "difficulty": "advanced"}, + ), + MemoryBankDocument( + document_id="doc3", + content="Data structures are fundamental to computer science.", + metadata={"category": "computer science", "difficulty": "intermediate"}, + ), + MemoryBankDocument( + document_id="doc4", + content="Neural networks are inspired by biological neural networks.", + metadata={"category": "AI", "difficulty": "advanced"}, + ), + ] + chunks = [] + for doc in docs: + chunks.extend( + make_overlapped_chunks( + doc.document_id, doc.content, window_len=512, overlap_len=64 + ) + ) + return chunks + + +async def register_vector_db(vector_dbs_impl: VectorDB, embedding_model: str): + vector_db_id = f"test_vector_db_{uuid.uuid4().hex}" + return await vector_dbs_impl.register_vector_db( + vector_db_id=vector_db_id, + embedding_model=embedding_model, + embedding_dimension=384, + ) + + +class TestVectorIO: + @pytest.mark.asyncio + async def test_banks_list(self, vector_io_stack, embedding_model): + _, vector_dbs_impl = vector_io_stack + + # Register a test bank + registered_vector_db = await register_vector_db( + vector_dbs_impl, embedding_model + ) + + try: + # Verify our bank shows up in list + response = await vector_dbs_impl.list_vector_dbs() + assert isinstance(response, ListVectorDBsResponse) + assert any( + vector_db.vector_db_id == registered_vector_db.vector_db_id + for vector_db in response.data + ) + finally: + # Clean up + await vector_dbs_impl.unregister_vector_db( + registered_vector_db.vector_db_id + ) + + # Verify our bank was removed + response = await vector_dbs_impl.list_vector_dbs() + assert isinstance(response, ListVectorDBsResponse) + assert all( + vector_db.vector_db_id != registered_vector_db.vector_db_id + for vector_db in response.data + ) + + @pytest.mark.asyncio + async def test_banks_register(self, vector_io_stack, embedding_model): + _, vector_dbs_impl = vector_io_stack + + vector_db_id = f"test_vector_db_{uuid.uuid4().hex}" + + try: + # Register initial bank + await vector_dbs_impl.register_vector_db( + vector_db_id=vector_db_id, + embedding_model=embedding_model, + embedding_dimension=384, + ) + + # Verify our bank exists + response = await vector_dbs_impl.list_vector_dbs() + assert isinstance(response, ListVectorDBsResponse) + assert any( + vector_db.vector_db_id == vector_db_id for vector_db in response.data + ) + + # Try registering same bank again + await vector_dbs_impl.register_vector_db( + vector_db_id=vector_db_id, + embedding_model=embedding_model, + embedding_dimension=384, + ) + + # Verify still only one instance of our bank + response = await vector_dbs_impl.list_vector_dbs() + assert isinstance(response, ListVectorDBsResponse) + assert ( + len( + [ + vector_db + for vector_db in response.data + if vector_db.vector_db_id == vector_db_id + ] + ) + == 1 + ) + finally: + # Clean up + await vector_dbs_impl.unregister_vector_db(vector_db_id) + + @pytest.mark.asyncio + async def test_query_documents( + self, vector_io_stack, embedding_model, sample_chunks + ): + vector_io_impl, vector_dbs_impl = vector_io_stack + + with pytest.raises(ValueError): + await vector_io_impl.insert_chunks("test_vector_db", sample_chunks) + + registered_db = await register_vector_db(vector_dbs_impl, embedding_model) + await vector_io_impl.insert_chunks(registered_db.vector_db_id, sample_chunks) + + query1 = "programming language" + response1 = await vector_io_impl.query_chunks( + registered_db.vector_db_id, query1 + ) + assert_valid_response(response1) + assert any("Python" in chunk.content for chunk in response1.chunks) + + # Test case 3: Query with semantic similarity + query3 = "AI and brain-inspired computing" + response3 = await vector_io_impl.query_chunks( + registered_db.vector_db_id, query3 + ) + assert_valid_response(response3) + assert any( + "neural networks" in chunk.content.lower() for chunk in response3.chunks + ) + + # Test case 4: Query with limit on number of results + query4 = "computer" + params4 = {"max_chunks": 2} + response4 = await vector_io_impl.query_chunks( + registered_db.vector_db_id, query4, params4 + ) + assert_valid_response(response4) + assert len(response4.chunks) <= 2 + + # Test case 5: Query with threshold on similarity score + query5 = "quantum computing" # Not directly related to any document + params5 = {"score_threshold": 0.01} + response5 = await vector_io_impl.query_chunks( + registered_db.vector_db_id, query5, params5 + ) + assert_valid_response(response5) + print("The scores are:", response5.scores) + assert all(score >= 0.01 for score in response5.scores) + + +def assert_valid_response(response: QueryChunksResponse): + assert len(response.chunks) > 0 + assert len(response.scores) > 0 + assert len(response.chunks) == len(response.scores) + for chunk in response.chunks: + assert isinstance(chunk.content, str) diff --git a/llama_stack/providers/tests/memory/test_vector_store.py b/llama_stack/providers/tests/vector_io/test_vector_store.py similarity index 94% rename from llama_stack/providers/tests/memory/test_vector_store.py rename to llama_stack/providers/tests/vector_io/test_vector_store.py index 1ad7abf0c..ef6bfca73 100644 --- a/llama_stack/providers/tests/memory/test_vector_store.py +++ b/llama_stack/providers/tests/vector_io/test_vector_store.py @@ -11,8 +11,11 @@ from pathlib import Path import pytest -from llama_stack.apis.memory.memory import MemoryBankDocument, URL -from llama_stack.providers.utils.memory.vector_store import content_from_doc +from llama_stack.providers.utils.memory.vector_store import ( + content_from_doc, + MemoryBankDocument, + URL, +) DUMMY_PDF_PATH = Path(os.path.abspath(__file__)).parent / "fixtures" / "dummy.pdf" diff --git a/llama_stack/providers/utils/memory/vector_store.py b/llama_stack/providers/utils/memory/vector_store.py index c97633558..c2de6c714 100644 --- a/llama_stack/providers/utils/memory/vector_store.py +++ b/llama_stack/providers/utils/memory/vector_store.py @@ -18,6 +18,8 @@ import numpy as np from llama_models.llama3.api.tokenizer import Tokenizer from numpy.typing import NDArray + +from pydantic import BaseModel, Field from pypdf import PdfReader from llama_stack.apis.common.content_types import ( @@ -25,16 +27,24 @@ from llama_stack.apis.common.content_types import ( TextContentItem, URL, ) -from llama_stack.apis.memory import Chunk, MemoryBankDocument, QueryDocumentsResponse -from llama_stack.apis.memory_banks import VectorMemoryBank +from llama_stack.apis.vector_dbs import VectorDB +from llama_stack.apis.vector_io import Chunk, QueryChunksResponse from llama_stack.providers.datatypes import Api from llama_stack.providers.utils.inference.prompt_adapter import ( interleaved_content_as_str, ) + log = logging.getLogger(__name__) +class MemoryBankDocument(BaseModel): + document_id: str + content: InterleavedContent | URL + mime_type: str | None = None + metadata: Dict[str, Any] = Field(default_factory=dict) + + def parse_pdf(data: bytes) -> str: # For PDF and DOC/DOCX files, we can't reliably convert to string pdf_bytes = io.BytesIO(data) @@ -165,7 +175,7 @@ class EmbeddingIndex(ABC): @abstractmethod async def query( self, embedding: NDArray, k: int, score_threshold: float - ) -> QueryDocumentsResponse: + ) -> QueryChunksResponse: raise NotImplementedError() @abstractmethod @@ -174,56 +184,35 @@ class EmbeddingIndex(ABC): @dataclass -class BankWithIndex: - bank: VectorMemoryBank +class VectorDBWithIndex: + vector_db: VectorDB index: EmbeddingIndex inference_api: Api.inference - async def insert_documents( + async def insert_chunks( self, - documents: List[MemoryBankDocument], + chunks: List[Chunk], ) -> None: - for doc in documents: - content = await content_from_doc(doc) - chunks = make_overlapped_chunks( - doc.document_id, - content, - self.bank.chunk_size_in_tokens, - self.bank.overlap_size_in_tokens - or (self.bank.chunk_size_in_tokens // 4), - ) - if not chunks: - continue - embeddings_response = await self.inference_api.embeddings( - self.bank.embedding_model, [x.content for x in chunks] - ) - embeddings = np.array(embeddings_response.embeddings) + embeddings_response = await self.inference_api.embeddings( + self.vector_db.embedding_model, [x.content for x in chunks] + ) + embeddings = np.array(embeddings_response.embeddings) - await self.index.add_chunks(chunks, embeddings) + await self.index.add_chunks(chunks, embeddings) - async def query_documents( + async def query_chunks( self, query: InterleavedContent, params: Optional[Dict[str, Any]] = None, - ) -> QueryDocumentsResponse: + ) -> QueryChunksResponse: if params is None: params = {} k = params.get("max_chunks", 3) score_threshold = params.get("score_threshold", 0.0) - def _process(c) -> str: - if isinstance(c, str): - return c - else: - return "" - - if isinstance(query, list): - query_str = " ".join([_process(c) for c in query]) - else: - query_str = _process(query) - + query_str = interleaved_content_as_str(query) embeddings_response = await self.inference_api.embeddings( - self.bank.embedding_model, [query_str] + self.vector_db.embedding_model, [query_str] ) query_vector = np.array(embeddings_response.embeddings[0], dtype=np.float32) return await self.index.query(query_vector, k, score_threshold) diff --git a/tests/client-sdk/conftest.py b/tests/client-sdk/conftest.py index 0b5324c0e..c19546887 100644 --- a/tests/client-sdk/conftest.py +++ b/tests/client-sdk/conftest.py @@ -32,6 +32,7 @@ def pytest_addoption(parser): TEXT_MODEL = "meta-llama/Llama-3.1-8B-Instruct" INFERENCE_MODEL = "meta-llama/Llama-3.2-11B-Vision-Instruct" + @pytest.fixture(scope="session") def provider_data(): # check env for tavily secret, brave secret and inject all into provider data diff --git a/tests/client-sdk/memory/__init__.py b/tests/client-sdk/vector_io/__init__.py similarity index 100% rename from tests/client-sdk/memory/__init__.py rename to tests/client-sdk/vector_io/__init__.py diff --git a/tests/client-sdk/memory/test_memory.py b/tests/client-sdk/vector_io/test_vector_io.py similarity index 100% rename from tests/client-sdk/memory/test_memory.py rename to tests/client-sdk/vector_io/test_vector_io.py From 1a7490470a4622f53f17485a548899b5cf501396 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 10:04:16 -0800 Subject: [PATCH 071/107] [memory refactor][3/n] Introduce RAGToolRuntime as a specialized sub-protocol (#832) See https://github.com/meta-llama/llama-stack/issues/827 for the broader design. Third part: - we need to make `tool_runtime.rag_tool.query_context()` and `tool_runtime.rag_tool.insert_documents()` methods work smoothly with complete type safety. To that end, we introduce a sub-resource path `tool-runtime/rag-tool/` and make changes to the resolver to make things work. - the PR updates the agents implementation to directly call these typed APIs for memory accesses rather than going through the complex, untyped "invoke_tool" API. the code looks much nicer and simpler (expectedly.) - there are a number of hacks in the server resolver implementation still, we will live with some and fix some Note that we must make sure the client SDKs are able to handle this subresource complexity also. Stainless has support for subresources, so this should be possible but beware. ## Test Plan Our RAG test is sad (doesn't actually test for actual RAG output) but I verified that the implementation works. I will work on fixing the RAG test afterwards. ```bash pytest -s -v tests/agents/test_agents.py -k "rag and together" --safety-shield=meta-llama/Llama-Guard-3-8B ``` --- .../openapi_generator/pyopenapi/operations.py | 6 + docs/resources/llama-stack-spec.html | 1191 +++++++++-------- docs/resources/llama-stack-spec.yaml | 997 +++++++------- llama_stack/apis/tools/__init__.py | 1 + llama_stack/apis/tools/rag_tool.py | 95 ++ llama_stack/apis/tools/tools.py | 10 +- llama_stack/distribution/resolver.py | 2 + llama_stack/distribution/routers/routers.py | 46 +- llama_stack/distribution/server/endpoints.py | 22 +- llama_stack/distribution/stack.py | 3 +- llama_stack/distribution/store/registry.py | 2 +- .../inline/agents/meta_reference/__init__.py | 3 +- .../agents/meta_reference/agent_instance.py | 95 +- .../inline/agents/meta_reference/agents.py | 12 +- .../code_interpreter/code_interpreter.py | 4 +- .../inline/tool_runtime/memory/__init__.py | 4 +- .../inline/tool_runtime/memory/config.py | 83 +- .../tool_runtime/memory/context_retriever.py | 52 +- .../inline/tool_runtime/memory/memory.py | 174 ++- .../providers/registry/tool_runtime.py | 2 +- .../tool_runtime/bing_search/bing_search.py | 4 +- .../tool_runtime/brave_search/brave_search.py | 4 +- .../model_context_protocol.py | 4 +- .../tavily_search/tavily_search.py | 4 +- .../wolfram_alpha/wolfram_alpha.py | 4 +- .../providers/tests/agents/conftest.py | 14 +- .../providers/tests/agents/fixtures.py | 4 +- .../providers/tests/agents/test_agents.py | 4 +- .../tests/vector_io/test_vector_io.py | 15 +- .../providers/utils/memory/vector_store.py | 20 +- llama_stack/scripts/test_rag_via_curl.py | 105 ++ llama_stack/templates/together/build.yaml | 2 +- llama_stack/templates/together/run.yaml | 5 +- 33 files changed, 1648 insertions(+), 1345 deletions(-) create mode 100644 llama_stack/apis/tools/rag_tool.py create mode 100644 llama_stack/scripts/test_rag_via_curl.py diff --git a/docs/openapi_generator/pyopenapi/operations.py b/docs/openapi_generator/pyopenapi/operations.py index 4cea9d970..abeb16936 100644 --- a/docs/openapi_generator/pyopenapi/operations.py +++ b/docs/openapi_generator/pyopenapi/operations.py @@ -172,10 +172,16 @@ def _get_endpoint_functions( def _get_defining_class(member_fn: str, derived_cls: type) -> type: "Find the class in which a member function is first defined in a class inheritance hierarchy." + # This import must be dynamic here + from llama_stack.apis.tools import RAGToolRuntime, ToolRuntime + # iterate in reverse member resolution order to find most specific class first for cls in reversed(inspect.getmro(derived_cls)): for name, _ in inspect.getmembers(cls, inspect.isfunction): if name == member_fn: + # HACK ALERT + if cls == RAGToolRuntime: + return ToolRuntime return cls raise ValidationError( diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index 459a53888..f00d7b291 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -1108,98 +1108,6 @@ ] } }, - "/v1/memory-banks/{memory_bank_id}": { - "get": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/MemoryBank" - }, - { - "type": "null" - } - ] - } - } - } - } - }, - "tags": [ - "MemoryBanks" - ], - "parameters": [ - { - "name": "memory_bank_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ] - }, - "delete": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "MemoryBanks" - ], - "parameters": [ - { - "name": "memory_bank_id", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ] - } - }, "/v1/models/{model_id}": { "get": { "responses": { @@ -1848,6 +1756,98 @@ ] } }, + "/v1/vector-dbs/{vector_db_id}": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "oneOf": [ + { + "$ref": "#/components/schemas/VectorDB" + }, + { + "type": "null" + } + ] + } + } + } + } + }, + "tags": [ + "VectorDBs" + ], + "parameters": [ + { + "name": "vector_db_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + }, + "delete": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "VectorDBs" + ], + "parameters": [ + { + "name": "vector_db_id", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + } + }, "/v1/health": { "get": { "responses": { @@ -1887,7 +1887,7 @@ ] } }, - "/v1/memory/insert": { + "/v1/vector-io/insert": { "post": { "responses": { "200": { @@ -1895,7 +1895,7 @@ } }, "tags": [ - "Memory" + "VectorIO" ], "parameters": [ { @@ -1917,6 +1917,49 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InsertChunksRequest" + } + } + }, + "required": true + } + } + }, + "/v1/tool-runtime/rag-tool/insert-documents": { + "post": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "ToolRuntime" + ], + "summary": "Index documents so they can be used by the RAG system", + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], "requestBody": { "content": { "application/json": { @@ -2300,105 +2343,6 @@ } } }, - "/v1/memory-banks": { - "get": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListMemoryBanksResponse" - } - } - } - } - }, - "tags": [ - "MemoryBanks" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ] - }, - "post": { - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "oneOf": [ - { - "$ref": "#/components/schemas/VectorMemoryBank" - }, - { - "$ref": "#/components/schemas/KeyValueMemoryBank" - }, - { - "$ref": "#/components/schemas/KeywordMemoryBank" - }, - { - "$ref": "#/components/schemas/GraphMemoryBank" - } - ] - } - } - } - } - }, - "tags": [ - "MemoryBanks" - ], - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RegisterMemoryBankRequest" - } - } - }, - "required": true - } - } - }, "/v1/models": { "get": { "responses": { @@ -2912,6 +2856,92 @@ ] } }, + "/v1/vector-dbs": { + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListVectorDBsResponse" + } + } + } + } + }, + "tags": [ + "VectorDBs" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ] + }, + "post": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VectorDB" + } + } + } + } + }, + "tags": [ + "VectorDBs" + ], + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RegisterVectorDbRequest" + } + } + }, + "required": true + } + } + }, "/v1/telemetry/events": { "post": { "responses": { @@ -3003,7 +3033,7 @@ } } }, - "/v1/memory/query": { + "/v1/vector-io/query": { "post": { "responses": { "200": { @@ -3011,14 +3041,14 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QueryDocumentsResponse" + "$ref": "#/components/schemas/QueryChunksResponse" } } } } }, "tags": [ - "Memory" + "VectorIO" ], "parameters": [ { @@ -3044,7 +3074,57 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QueryDocumentsRequest" + "$ref": "#/components/schemas/QueryChunksRequest" + } + } + }, + "required": true + } + } + }, + "/v1/tool-runtime/rag-tool/query-context": { + "post": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RAGQueryResult" + } + } + } + } + }, + "tags": [ + "ToolRuntime" + ], + "summary": "Query the RAG system for context; typically invoked by the agent", + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryContextRequest" } } }, @@ -5851,118 +5931,6 @@ "aggregated_results" ] }, - "GraphMemoryBank": { - "type": "object", - "properties": { - "identifier": { - "type": "string" - }, - "provider_resource_id": { - "type": "string" - }, - "provider_id": { - "type": "string" - }, - "type": { - "type": "string", - "const": "memory_bank", - "default": "memory_bank" - }, - "memory_bank_type": { - "type": "string", - "const": "graph", - "default": "graph" - } - }, - "additionalProperties": false, - "required": [ - "identifier", - "provider_resource_id", - "provider_id", - "type", - "memory_bank_type" - ] - }, - "KeyValueMemoryBank": { - "type": "object", - "properties": { - "identifier": { - "type": "string" - }, - "provider_resource_id": { - "type": "string" - }, - "provider_id": { - "type": "string" - }, - "type": { - "type": "string", - "const": "memory_bank", - "default": "memory_bank" - }, - "memory_bank_type": { - "type": "string", - "const": "keyvalue", - "default": "keyvalue" - } - }, - "additionalProperties": false, - "required": [ - "identifier", - "provider_resource_id", - "provider_id", - "type", - "memory_bank_type" - ] - }, - "KeywordMemoryBank": { - "type": "object", - "properties": { - "identifier": { - "type": "string" - }, - "provider_resource_id": { - "type": "string" - }, - "provider_id": { - "type": "string" - }, - "type": { - "type": "string", - "const": "memory_bank", - "default": "memory_bank" - }, - "memory_bank_type": { - "type": "string", - "const": "keyword", - "default": "keyword" - } - }, - "additionalProperties": false, - "required": [ - "identifier", - "provider_resource_id", - "provider_id", - "type", - "memory_bank_type" - ] - }, - "MemoryBank": { - "oneOf": [ - { - "$ref": "#/components/schemas/VectorMemoryBank" - }, - { - "$ref": "#/components/schemas/KeyValueMemoryBank" - }, - { - "$ref": "#/components/schemas/KeywordMemoryBank" - }, - { - "$ref": "#/components/schemas/GraphMemoryBank" - } - ] - }, "Session": { "type": "object", "properties": { @@ -5981,9 +5949,6 @@ "started_at": { "type": "string", "format": "date-time" - }, - "memory_bank": { - "$ref": "#/components/schemas/MemoryBank" } }, "additionalProperties": false, @@ -5995,53 +5960,6 @@ ], "title": "A single session of an interaction with an Agentic System." }, - "VectorMemoryBank": { - "type": "object", - "properties": { - "identifier": { - "type": "string" - }, - "provider_resource_id": { - "type": "string" - }, - "provider_id": { - "type": "string" - }, - "type": { - "type": "string", - "const": "memory_bank", - "default": "memory_bank" - }, - "memory_bank_type": { - "type": "string", - "const": "vector", - "default": "vector" - }, - "embedding_model": { - "type": "string" - }, - "chunk_size_in_tokens": { - "type": "integer" - }, - "embedding_dimension": { - "type": "integer", - "default": 384 - }, - "overlap_size_in_tokens": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "identifier", - "provider_resource_id", - "provider_id", - "type", - "memory_bank_type", - "embedding_model", - "chunk_size_in_tokens" - ] - }, "AgentStepResponse": { "type": "object", "properties": { @@ -7012,6 +6930,40 @@ "data" ] }, + "VectorDB": { + "type": "object", + "properties": { + "identifier": { + "type": "string" + }, + "provider_resource_id": { + "type": "string" + }, + "provider_id": { + "type": "string" + }, + "type": { + "type": "string", + "const": "vector_db", + "default": "vector_db" + }, + "embedding_model": { + "type": "string" + }, + "embedding_dimension": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "identifier", + "provider_resource_id", + "provider_id", + "type", + "embedding_model", + "embedding_dimension" + ] + }, "HealthInfo": { "type": "object", "properties": { @@ -7024,7 +6976,64 @@ "status" ] }, - "MemoryBankDocument": { + "InsertChunksRequest": { + "type": "object", + "properties": { + "vector_db_id": { + "type": "string" + }, + "chunks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "content": { + "$ref": "#/components/schemas/InterleavedContent" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "array" + }, + { + "type": "object" + } + ] + } + } + }, + "additionalProperties": false, + "required": [ + "content", + "metadata" + ] + } + }, + "ttl_seconds": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "vector_db_id", + "chunks" + ] + }, + "RAGDocument": { "type": "object", "properties": { "document_id": { @@ -7088,23 +7097,24 @@ "InsertDocumentsRequest": { "type": "object", "properties": { - "bank_id": { - "type": "string" - }, "documents": { "type": "array", "items": { - "$ref": "#/components/schemas/MemoryBankDocument" + "$ref": "#/components/schemas/RAGDocument" } }, - "ttl_seconds": { + "vector_db_id": { + "type": "string" + }, + "chunk_size_in_tokens": { "type": "integer" } }, "additionalProperties": false, "required": [ - "bank_id", - "documents" + "documents", + "vector_db_id", + "chunk_size_in_tokens" ] }, "InvokeToolRequest": { @@ -7113,7 +7123,7 @@ "tool_name": { "type": "string" }, - "args": { + "kwargs": { "type": "object", "additionalProperties": { "oneOf": [ @@ -7142,7 +7152,7 @@ "additionalProperties": false, "required": [ "tool_name", - "args" + "kwargs" ] }, "ToolInvocationResult": { @@ -7193,21 +7203,6 @@ "data" ] }, - "ListMemoryBanksResponse": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/components/schemas/MemoryBank" - } - } - }, - "additionalProperties": false, - "required": [ - "data" - ] - }, "ListModelsResponse": { "type": "object", "properties": { @@ -7356,6 +7351,21 @@ "data" ] }, + "ListVectorDBsResponse": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VectorDB" + } + } + }, + "additionalProperties": false, + "required": [ + "data" + ] + }, "LogSeverity": { "type": "string", "enum": [ @@ -7873,10 +7883,10 @@ "job_uuid" ] }, - "QueryDocumentsRequest": { + "QueryChunksRequest": { "type": "object", "properties": { - "bank_id": { + "vector_db_id": { "type": "string" }, "query": { @@ -7910,11 +7920,11 @@ }, "additionalProperties": false, "required": [ - "bank_id", + "vector_db_id", "query" ] }, - "QueryDocumentsResponse": { + "QueryChunksResponse": { "type": "object", "properties": { "chunks": { @@ -7925,18 +7935,36 @@ "content": { "$ref": "#/components/schemas/InterleavedContent" }, - "token_count": { - "type": "integer" - }, - "document_id": { - "type": "string" + "metadata": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "array" + }, + { + "type": "object" + } + ] + } } }, "additionalProperties": false, "required": [ "content", - "token_count", - "document_id" + "metadata" ] } }, @@ -7953,6 +7981,111 @@ "scores" ] }, + "DefaultRAGQueryGeneratorConfig": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "default", + "default": "default" + }, + "separator": { + "type": "string", + "default": " " + } + }, + "additionalProperties": false, + "required": [ + "type", + "separator" + ] + }, + "LLMRAGQueryGeneratorConfig": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "llm", + "default": "llm" + }, + "model": { + "type": "string" + }, + "template": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "type", + "model", + "template" + ] + }, + "RAGQueryConfig": { + "type": "object", + "properties": { + "query_generator_config": { + "$ref": "#/components/schemas/RAGQueryGeneratorConfig" + }, + "max_tokens_in_context": { + "type": "integer", + "default": 4096 + }, + "max_chunks": { + "type": "integer", + "default": 5 + } + }, + "additionalProperties": false, + "required": [ + "query_generator_config", + "max_tokens_in_context", + "max_chunks" + ] + }, + "RAGQueryGeneratorConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/DefaultRAGQueryGeneratorConfig" + }, + { + "$ref": "#/components/schemas/LLMRAGQueryGeneratorConfig" + } + ] + }, + "QueryContextRequest": { + "type": "object", + "properties": { + "content": { + "$ref": "#/components/schemas/InterleavedContent" + }, + "query_config": { + "$ref": "#/components/schemas/RAGQueryConfig" + }, + "vector_db_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "content", + "query_config", + "vector_db_ids" + ] + }, + "RAGQueryResult": { + "type": "object", + "properties": { + "content": { + "$ref": "#/components/schemas/InterleavedContent" + } + }, + "additionalProperties": false + }, "QueryCondition": { "type": "object", "properties": { @@ -8139,108 +8272,6 @@ "scoring_functions" ] }, - "GraphMemoryBankParams": { - "type": "object", - "properties": { - "memory_bank_type": { - "type": "string", - "const": "graph", - "default": "graph" - } - }, - "additionalProperties": false, - "required": [ - "memory_bank_type" - ] - }, - "KeyValueMemoryBankParams": { - "type": "object", - "properties": { - "memory_bank_type": { - "type": "string", - "const": "keyvalue", - "default": "keyvalue" - } - }, - "additionalProperties": false, - "required": [ - "memory_bank_type" - ] - }, - "KeywordMemoryBankParams": { - "type": "object", - "properties": { - "memory_bank_type": { - "type": "string", - "const": "keyword", - "default": "keyword" - } - }, - "additionalProperties": false, - "required": [ - "memory_bank_type" - ] - }, - "VectorMemoryBankParams": { - "type": "object", - "properties": { - "memory_bank_type": { - "type": "string", - "const": "vector", - "default": "vector" - }, - "embedding_model": { - "type": "string" - }, - "chunk_size_in_tokens": { - "type": "integer" - }, - "overlap_size_in_tokens": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "memory_bank_type", - "embedding_model", - "chunk_size_in_tokens" - ] - }, - "RegisterMemoryBankRequest": { - "type": "object", - "properties": { - "memory_bank_id": { - "type": "string" - }, - "params": { - "oneOf": [ - { - "$ref": "#/components/schemas/VectorMemoryBankParams" - }, - { - "$ref": "#/components/schemas/KeyValueMemoryBankParams" - }, - { - "$ref": "#/components/schemas/KeywordMemoryBankParams" - }, - { - "$ref": "#/components/schemas/GraphMemoryBankParams" - } - ] - }, - "provider_id": { - "type": "string" - }, - "provider_memory_bank_id": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "memory_bank_id", - "params" - ] - }, "RegisterModelRequest": { "type": "object", "properties": { @@ -8413,6 +8444,31 @@ "provider_id" ] }, + "RegisterVectorDbRequest": { + "type": "object", + "properties": { + "vector_db_id": { + "type": "string" + }, + "embedding_model": { + "type": "string" + }, + "embedding_dimension": { + "type": "integer" + }, + "provider_id": { + "type": "string" + }, + "provider_vector_db_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "vector_db_id", + "embedding_model" + ] + }, "RunEvalRequest": { "type": "object", "properties": { @@ -9128,6 +9184,10 @@ { "name": "Datasets" }, + { + "name": "DefaultRAGQueryGeneratorConfig", + "description": "" + }, { "name": "EfficiencyConfig", "description": "" @@ -9158,14 +9218,6 @@ "name": "EvaluateRowsRequest", "description": "" }, - { - "name": "GraphMemoryBank", - "description": "" - }, - { - "name": "GraphMemoryBankParams", - "description": "" - }, { "name": "GreedySamplingStrategy", "description": "" @@ -9189,6 +9241,10 @@ "name": "InferenceStep", "description": "" }, + { + "name": "InsertChunksRequest", + "description": "" + }, { "name": "InsertDocumentsRequest", "description": "" @@ -9220,26 +9276,14 @@ "name": "JsonType", "description": "" }, - { - "name": "KeyValueMemoryBank", - "description": "" - }, - { - "name": "KeyValueMemoryBankParams", - "description": "" - }, - { - "name": "KeywordMemoryBank", - "description": "" - }, - { - "name": "KeywordMemoryBankParams", - "description": "" - }, { "name": "LLMAsJudgeScoringFnParams", "description": "" }, + { + "name": "LLMRAGQueryGeneratorConfig", + "description": "" + }, { "name": "ListDatasetsResponse", "description": "" @@ -9248,10 +9292,6 @@ "name": "ListEvalTasksResponse", "description": "" }, - { - "name": "ListMemoryBanksResponse", - "description": "" - }, { "name": "ListModelsResponse", "description": "" @@ -9284,6 +9324,10 @@ "name": "ListToolsResponse", "description": "" }, + { + "name": "ListVectorDBsResponse", + "description": "" + }, { "name": "LogEventRequest", "description": "" @@ -9296,20 +9340,6 @@ "name": "LoraFinetuningConfig", "description": "" }, - { - "name": "Memory" - }, - { - "name": "MemoryBank", - "description": "" - }, - { - "name": "MemoryBankDocument", - "description": "" - }, - { - "name": "MemoryBanks" - }, { "name": "MemoryRetrievalStep", "description": "" @@ -9388,6 +9418,14 @@ "name": "QATFinetuningConfig", "description": "" }, + { + "name": "QueryChunksRequest", + "description": "" + }, + { + "name": "QueryChunksResponse", + "description": "" + }, { "name": "QueryCondition", "description": "" @@ -9397,12 +9435,8 @@ "description": "" }, { - "name": "QueryDocumentsRequest", - "description": "" - }, - { - "name": "QueryDocumentsResponse", - "description": "" + "name": "QueryContextRequest", + "description": "" }, { "name": "QuerySpanTreeResponse", @@ -9416,6 +9450,22 @@ "name": "QueryTracesResponse", "description": "" }, + { + "name": "RAGDocument", + "description": "" + }, + { + "name": "RAGQueryConfig", + "description": "" + }, + { + "name": "RAGQueryGeneratorConfig", + "description": "" + }, + { + "name": "RAGQueryResult", + "description": "" + }, { "name": "RegexParserScoringFnParams", "description": "" @@ -9428,10 +9478,6 @@ "name": "RegisterEvalTaskRequest", "description": "" }, - { - "name": "RegisterMemoryBankRequest", - "description": "" - }, { "name": "RegisterModelRequest", "description": "" @@ -9448,6 +9494,10 @@ "name": "RegisterToolGroupRequest", "description": "" }, + { + "name": "RegisterVectorDbRequest", + "description": "" + }, { "name": "ResponseFormat", "description": "" @@ -9701,12 +9751,14 @@ "description": "" }, { - "name": "VectorMemoryBank", - "description": "" + "name": "VectorDB", + "description": "" }, { - "name": "VectorMemoryBankParams", - "description": "" + "name": "VectorDBs" + }, + { + "name": "VectorIO" }, { "name": "VersionInfo", @@ -9729,8 +9781,6 @@ "EvalTasks", "Inference", "Inspect", - "Memory", - "MemoryBanks", "Models", "PostTraining (Coming Soon)", "Safety", @@ -9740,7 +9790,9 @@ "SyntheticDataGeneration (Coming Soon)", "Telemetry", "ToolGroups", - "ToolRuntime" + "ToolRuntime", + "VectorDBs", + "VectorIO" ] }, { @@ -9793,19 +9845,19 @@ "DataConfig", "Dataset", "DatasetFormat", + "DefaultRAGQueryGeneratorConfig", "EfficiencyConfig", "EmbeddingsRequest", "EmbeddingsResponse", "EvalTask", "EvaluateResponse", "EvaluateRowsRequest", - "GraphMemoryBank", - "GraphMemoryBankParams", "GreedySamplingStrategy", "HealthInfo", "ImageContentItem", "ImageDelta", "InferenceStep", + "InsertChunksRequest", "InsertDocumentsRequest", "InterleavedContent", "InterleavedContentItem", @@ -9813,14 +9865,10 @@ "Job", "JobStatus", "JsonType", - "KeyValueMemoryBank", - "KeyValueMemoryBankParams", - "KeywordMemoryBank", - "KeywordMemoryBankParams", "LLMAsJudgeScoringFnParams", + "LLMRAGQueryGeneratorConfig", "ListDatasetsResponse", "ListEvalTasksResponse", - "ListMemoryBanksResponse", "ListModelsResponse", "ListPostTrainingJobsResponse", "ListProvidersResponse", @@ -9829,11 +9877,10 @@ "ListShieldsResponse", "ListToolGroupsResponse", "ListToolsResponse", + "ListVectorDBsResponse", "LogEventRequest", "LogSeverity", "LoraFinetuningConfig", - "MemoryBank", - "MemoryBankDocument", "MemoryRetrievalStep", "Message", "MetricEvent", @@ -9852,21 +9899,26 @@ "PreferenceOptimizeRequest", "ProviderInfo", "QATFinetuningConfig", + "QueryChunksRequest", + "QueryChunksResponse", "QueryCondition", "QueryConditionOp", - "QueryDocumentsRequest", - "QueryDocumentsResponse", + "QueryContextRequest", "QuerySpanTreeResponse", "QuerySpansResponse", "QueryTracesResponse", + "RAGDocument", + "RAGQueryConfig", + "RAGQueryGeneratorConfig", + "RAGQueryResult", "RegexParserScoringFnParams", "RegisterDatasetRequest", "RegisterEvalTaskRequest", - "RegisterMemoryBankRequest", "RegisterModelRequest", "RegisterScoringFunctionRequest", "RegisterShieldRequest", "RegisterToolGroupRequest", + "RegisterVectorDbRequest", "ResponseFormat", "RouteInfo", "RunEvalRequest", @@ -9924,8 +9976,7 @@ "UnionType", "UnstructuredLogEvent", "UserMessage", - "VectorMemoryBank", - "VectorMemoryBankParams", + "VectorDB", "VersionInfo", "ViolationLevel" ] diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index 9aeac6db3..e1ae07c45 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -761,6 +761,20 @@ components: - instruct - dialog type: string + DefaultRAGQueryGeneratorConfig: + additionalProperties: false + properties: + separator: + default: ' ' + type: string + type: + const: default + default: default + type: string + required: + - type + - separator + type: object EfficiencyConfig: additionalProperties: false properties: @@ -891,40 +905,6 @@ components: - scoring_functions - task_config type: object - GraphMemoryBank: - additionalProperties: false - properties: - identifier: - type: string - memory_bank_type: - const: graph - default: graph - type: string - provider_id: - type: string - provider_resource_id: - type: string - type: - const: memory_bank - default: memory_bank - type: string - required: - - identifier - - provider_resource_id - - provider_id - - type - - memory_bank_type - type: object - GraphMemoryBankParams: - additionalProperties: false - properties: - memory_bank_type: - const: graph - default: graph - type: string - required: - - memory_bank_type - type: object GreedySamplingStrategy: additionalProperties: false properties: @@ -997,20 +977,53 @@ components: - step_type - model_response type: object - InsertDocumentsRequest: + InsertChunksRequest: additionalProperties: false properties: - bank_id: - type: string - documents: + chunks: items: - $ref: '#/components/schemas/MemoryBankDocument' + additionalProperties: false + properties: + content: + $ref: '#/components/schemas/InterleavedContent' + metadata: + additionalProperties: + oneOf: + - type: 'null' + - type: boolean + - type: number + - type: string + - type: array + - type: object + type: object + required: + - content + - metadata + type: object type: array ttl_seconds: type: integer + vector_db_id: + type: string + required: + - vector_db_id + - chunks + type: object + InsertDocumentsRequest: + additionalProperties: false + properties: + chunk_size_in_tokens: + type: integer + documents: + items: + $ref: '#/components/schemas/RAGDocument' + type: array + vector_db_id: + type: string required: - - bank_id - documents + - vector_db_id + - chunk_size_in_tokens type: object InterleavedContent: oneOf: @@ -1026,7 +1039,7 @@ components: InvokeToolRequest: additionalProperties: false properties: - args: + kwargs: additionalProperties: oneOf: - type: 'null' @@ -1040,7 +1053,7 @@ components: type: string required: - tool_name - - args + - kwargs type: object Job: additionalProperties: false @@ -1067,74 +1080,6 @@ components: required: - type type: object - KeyValueMemoryBank: - additionalProperties: false - properties: - identifier: - type: string - memory_bank_type: - const: keyvalue - default: keyvalue - type: string - provider_id: - type: string - provider_resource_id: - type: string - type: - const: memory_bank - default: memory_bank - type: string - required: - - identifier - - provider_resource_id - - provider_id - - type - - memory_bank_type - type: object - KeyValueMemoryBankParams: - additionalProperties: false - properties: - memory_bank_type: - const: keyvalue - default: keyvalue - type: string - required: - - memory_bank_type - type: object - KeywordMemoryBank: - additionalProperties: false - properties: - identifier: - type: string - memory_bank_type: - const: keyword - default: keyword - type: string - provider_id: - type: string - provider_resource_id: - type: string - type: - const: memory_bank - default: memory_bank - type: string - required: - - identifier - - provider_resource_id - - provider_id - - type - - memory_bank_type - type: object - KeywordMemoryBankParams: - additionalProperties: false - properties: - memory_bank_type: - const: keyword - default: keyword - type: string - required: - - memory_bank_type - type: object LLMAsJudgeScoringFnParams: additionalProperties: false properties: @@ -1158,6 +1103,22 @@ components: - type - judge_model type: object + LLMRAGQueryGeneratorConfig: + additionalProperties: false + properties: + model: + type: string + template: + type: string + type: + const: llm + default: llm + type: string + required: + - type + - model + - template + type: object ListDatasetsResponse: additionalProperties: false properties: @@ -1178,16 +1139,6 @@ components: required: - data type: object - ListMemoryBanksResponse: - additionalProperties: false - properties: - data: - items: - $ref: '#/components/schemas/MemoryBank' - type: array - required: - - data - type: object ListModelsResponse: additionalProperties: false properties: @@ -1274,6 +1225,16 @@ components: required: - data type: object + ListVectorDBsResponse: + additionalProperties: false + properties: + data: + items: + $ref: '#/components/schemas/VectorDB' + type: array + required: + - data + type: object LogEventRequest: additionalProperties: false properties: @@ -1330,42 +1291,6 @@ components: - rank - alpha type: object - MemoryBank: - oneOf: - - $ref: '#/components/schemas/VectorMemoryBank' - - $ref: '#/components/schemas/KeyValueMemoryBank' - - $ref: '#/components/schemas/KeywordMemoryBank' - - $ref: '#/components/schemas/GraphMemoryBank' - MemoryBankDocument: - additionalProperties: false - properties: - content: - oneOf: - - type: string - - $ref: '#/components/schemas/InterleavedContentItem' - - items: - $ref: '#/components/schemas/InterleavedContentItem' - type: array - - $ref: '#/components/schemas/URL' - document_id: - type: string - metadata: - additionalProperties: - oneOf: - - type: 'null' - - type: boolean - - type: number - - type: string - - type: array - - type: object - type: object - mime_type: - type: string - required: - - document_id - - content - - metadata - type: object MemoryRetrievalStep: additionalProperties: false properties: @@ -1705,6 +1630,59 @@ components: - quantizer_name - group_size type: object + QueryChunksRequest: + additionalProperties: false + properties: + params: + additionalProperties: + oneOf: + - type: 'null' + - type: boolean + - type: number + - type: string + - type: array + - type: object + type: object + query: + $ref: '#/components/schemas/InterleavedContent' + vector_db_id: + type: string + required: + - vector_db_id + - query + type: object + QueryChunksResponse: + additionalProperties: false + properties: + chunks: + items: + additionalProperties: false + properties: + content: + $ref: '#/components/schemas/InterleavedContent' + metadata: + additionalProperties: + oneOf: + - type: 'null' + - type: boolean + - type: number + - type: string + - type: array + - type: object + type: object + required: + - content + - metadata + type: object + type: array + scores: + items: + type: number + type: array + required: + - chunks + - scores + type: object QueryCondition: additionalProperties: false properties: @@ -1732,53 +1710,21 @@ components: - gt - lt type: string - QueryDocumentsRequest: + QueryContextRequest: additionalProperties: false properties: - bank_id: - type: string - params: - additionalProperties: - oneOf: - - type: 'null' - - type: boolean - - type: number - - type: string - - type: array - - type: object - type: object - query: + content: $ref: '#/components/schemas/InterleavedContent' - required: - - bank_id - - query - type: object - QueryDocumentsResponse: - additionalProperties: false - properties: - chunks: + query_config: + $ref: '#/components/schemas/RAGQueryConfig' + vector_db_ids: items: - additionalProperties: false - properties: - content: - $ref: '#/components/schemas/InterleavedContent' - document_id: - type: string - token_count: - type: integer - required: - - content - - token_count - - document_id - type: object - type: array - scores: - items: - type: number + type: string type: array required: - - chunks - - scores + - content + - query_config + - vector_db_ids type: object QuerySpanTreeResponse: additionalProperties: false @@ -1810,6 +1756,62 @@ components: required: - data type: object + RAGDocument: + additionalProperties: false + properties: + content: + oneOf: + - type: string + - $ref: '#/components/schemas/InterleavedContentItem' + - items: + $ref: '#/components/schemas/InterleavedContentItem' + type: array + - $ref: '#/components/schemas/URL' + document_id: + type: string + metadata: + additionalProperties: + oneOf: + - type: 'null' + - type: boolean + - type: number + - type: string + - type: array + - type: object + type: object + mime_type: + type: string + required: + - document_id + - content + - metadata + type: object + RAGQueryConfig: + additionalProperties: false + properties: + max_chunks: + default: 5 + type: integer + max_tokens_in_context: + default: 4096 + type: integer + query_generator_config: + $ref: '#/components/schemas/RAGQueryGeneratorConfig' + required: + - query_generator_config + - max_tokens_in_context + - max_chunks + type: object + RAGQueryGeneratorConfig: + oneOf: + - $ref: '#/components/schemas/DefaultRAGQueryGeneratorConfig' + - $ref: '#/components/schemas/LLMRAGQueryGeneratorConfig' + RAGQueryResult: + additionalProperties: false + properties: + content: + $ref: '#/components/schemas/InterleavedContent' + type: object RegexParserScoringFnParams: additionalProperties: false properties: @@ -1888,25 +1890,6 @@ components: - dataset_id - scoring_functions type: object - RegisterMemoryBankRequest: - additionalProperties: false - properties: - memory_bank_id: - type: string - params: - oneOf: - - $ref: '#/components/schemas/VectorMemoryBankParams' - - $ref: '#/components/schemas/KeyValueMemoryBankParams' - - $ref: '#/components/schemas/KeywordMemoryBankParams' - - $ref: '#/components/schemas/GraphMemoryBankParams' - provider_id: - type: string - provider_memory_bank_id: - type: string - required: - - memory_bank_id - - params - type: object RegisterModelRequest: additionalProperties: false properties: @@ -1999,6 +1982,23 @@ components: - toolgroup_id - provider_id type: object + RegisterVectorDbRequest: + additionalProperties: false + properties: + embedding_dimension: + type: integer + embedding_model: + type: string + provider_id: + type: string + provider_vector_db_id: + type: string + vector_db_id: + type: string + required: + - vector_db_id + - embedding_model + type: object ResponseFormat: oneOf: - additionalProperties: false @@ -2298,8 +2298,6 @@ components: Session: additionalProperties: false properties: - memory_bank: - $ref: '#/components/schemas/MemoryBank' session_id: type: string session_name: @@ -3202,58 +3200,30 @@ components: - role - content type: object - VectorMemoryBank: + VectorDB: additionalProperties: false properties: - chunk_size_in_tokens: - type: integer embedding_dimension: - default: 384 type: integer embedding_model: type: string identifier: type: string - memory_bank_type: - const: vector - default: vector - type: string - overlap_size_in_tokens: - type: integer provider_id: type: string provider_resource_id: type: string type: - const: memory_bank - default: memory_bank + const: vector_db + default: vector_db type: string required: - identifier - provider_resource_id - provider_id - type - - memory_bank_type - embedding_model - - chunk_size_in_tokens - type: object - VectorMemoryBankParams: - additionalProperties: false - properties: - chunk_size_in_tokens: - type: integer - embedding_model: - type: string - memory_bank_type: - const: vector - default: vector - type: string - overlap_size_in_tokens: - type: integer - required: - - memory_bank_type - - embedding_model - - chunk_size_in_tokens + - embedding_dimension type: object VersionInfo: additionalProperties: false @@ -4272,186 +4242,6 @@ paths: description: OK tags: - Inspect - /v1/memory-banks: - get: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/ListMemoryBanksResponse' - description: OK - tags: - - MemoryBanks - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/RegisterMemoryBankRequest' - required: true - responses: - '200': - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/VectorMemoryBank' - - $ref: '#/components/schemas/KeyValueMemoryBank' - - $ref: '#/components/schemas/KeywordMemoryBank' - - $ref: '#/components/schemas/GraphMemoryBank' - description: '' - tags: - - MemoryBanks - /v1/memory-banks/{memory_bank_id}: - delete: - parameters: - - in: path - name: memory_bank_id - required: true - schema: - type: string - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - description: OK - tags: - - MemoryBanks - get: - parameters: - - in: path - name: memory_bank_id - required: true - schema: - type: string - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - responses: - '200': - content: - application/json: - schema: - oneOf: - - $ref: '#/components/schemas/MemoryBank' - - type: 'null' - description: OK - tags: - - MemoryBanks - /v1/memory/insert: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/InsertDocumentsRequest' - required: true - responses: - '200': - description: OK - tags: - - Memory - /v1/memory/query: - post: - parameters: - - description: JSON-encoded provider data which will be made available to the - adapter servicing the API - in: header - name: X-LlamaStack-Provider-Data - required: false - schema: - type: string - - description: Version of the client making the request. This is used to ensure - that the client and server are compatible. - in: header - name: X-LlamaStack-Client-Version - required: false - schema: - type: string - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/QueryDocumentsRequest' - required: true - responses: - '200': - content: - application/json: - schema: - $ref: '#/components/schemas/QueryDocumentsResponse' - description: OK - tags: - - Memory /v1/models: get: parameters: @@ -5386,6 +5176,68 @@ paths: description: OK tags: - ToolRuntime + /v1/tool-runtime/rag-tool/insert-documents: + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InsertDocumentsRequest' + required: true + responses: + '200': + description: OK + summary: Index documents so they can be used by the RAG system + tags: + - ToolRuntime + /v1/tool-runtime/rag-tool/query-context: + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryContextRequest' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/RAGQueryResult' + description: OK + summary: Query the RAG system for context; typically invoked by the agent + tags: + - ToolRuntime /v1/toolgroups: get: parameters: @@ -5562,6 +5414,182 @@ paths: description: OK tags: - ToolGroups + /v1/vector-dbs: + get: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/ListVectorDBsResponse' + description: OK + tags: + - VectorDBs + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/RegisterVectorDbRequest' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/VectorDB' + description: OK + tags: + - VectorDBs + /v1/vector-dbs/{vector_db_id}: + delete: + parameters: + - in: path + name: vector_db_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + description: OK + tags: + - VectorDBs + get: + parameters: + - in: path + name: vector_db_id + required: true + schema: + type: string + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + responses: + '200': + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/VectorDB' + - type: 'null' + description: OK + tags: + - VectorDBs + /v1/vector-io/insert: + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/InsertChunksRequest' + required: true + responses: + '200': + description: OK + tags: + - VectorIO + /v1/vector-io/query: + post: + parameters: + - description: JSON-encoded provider data which will be made available to the + adapter servicing the API + in: header + name: X-LlamaStack-Provider-Data + required: false + schema: + type: string + - description: Version of the client making the request. This is used to ensure + that the client and server are compatible. + in: header + name: X-LlamaStack-Client-Version + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/QueryChunksRequest' + required: true + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/QueryChunksResponse' + description: OK + tags: + - VectorIO /v1/version: get: parameters: @@ -5748,6 +5776,9 @@ tags: name: DatasetFormat - name: DatasetIO - name: Datasets +- description: + name: DefaultRAGQueryGeneratorConfig - description: name: EfficiencyConfig @@ -5767,12 +5798,6 @@ tags: - description: name: EvaluateRowsRequest -- description: - name: GraphMemoryBank -- description: - name: GraphMemoryBankParams - description: name: GreedySamplingStrategy @@ -5786,6 +5811,9 @@ tags: - name: Inference - description: name: InferenceStep +- description: + name: InsertChunksRequest - description: name: InsertDocumentsRequest @@ -5805,30 +5833,18 @@ tags: name: JobStatus - description: name: JsonType -- description: - name: KeyValueMemoryBank -- description: - name: KeyValueMemoryBankParams -- description: - name: KeywordMemoryBank -- description: - name: KeywordMemoryBankParams - description: name: LLMAsJudgeScoringFnParams +- description: + name: LLMRAGQueryGeneratorConfig - description: name: ListDatasetsResponse - description: name: ListEvalTasksResponse -- description: - name: ListMemoryBanksResponse - description: name: ListModelsResponse @@ -5853,6 +5869,9 @@ tags: - description: name: ListToolsResponse +- description: + name: ListVectorDBsResponse - description: name: LogEventRequest @@ -5861,13 +5880,6 @@ tags: - description: name: LoraFinetuningConfig -- name: Memory -- description: - name: MemoryBank -- description: - name: MemoryBankDocument -- name: MemoryBanks - description: name: MemoryRetrievalStep @@ -5920,17 +5932,20 @@ tags: - description: name: QATFinetuningConfig +- description: + name: QueryChunksRequest +- description: + name: QueryChunksResponse - description: name: QueryCondition - description: name: QueryConditionOp -- description: - name: QueryDocumentsRequest -- description: - name: QueryDocumentsResponse + name: QueryContextRequest - description: name: QuerySpanTreeResponse @@ -5940,6 +5955,15 @@ tags: - description: name: QueryTracesResponse +- description: + name: RAGDocument +- description: + name: RAGQueryConfig +- description: + name: RAGQueryGeneratorConfig +- description: + name: RAGQueryResult - description: name: RegexParserScoringFnParams @@ -5949,9 +5973,6 @@ tags: - description: name: RegisterEvalTaskRequest -- description: - name: RegisterMemoryBankRequest - description: name: RegisterModelRequest @@ -5964,6 +5985,9 @@ tags: - description: name: RegisterToolGroupRequest +- description: + name: RegisterVectorDbRequest - description: name: ResponseFormat - description: @@ -6128,12 +6152,10 @@ tags: name: UnstructuredLogEvent - description: name: UserMessage -- description: - name: VectorMemoryBank -- description: - name: VectorMemoryBankParams +- description: + name: VectorDB +- name: VectorDBs +- name: VectorIO - description: name: VersionInfo - description: @@ -6149,8 +6171,6 @@ x-tagGroups: - EvalTasks - Inference - Inspect - - Memory - - MemoryBanks - Models - PostTraining (Coming Soon) - Safety @@ -6161,6 +6181,8 @@ x-tagGroups: - Telemetry - ToolGroups - ToolRuntime + - VectorDBs + - VectorIO - name: Types tags: - AgentCandidate @@ -6210,19 +6232,19 @@ x-tagGroups: - DataConfig - Dataset - DatasetFormat + - DefaultRAGQueryGeneratorConfig - EfficiencyConfig - EmbeddingsRequest - EmbeddingsResponse - EvalTask - EvaluateResponse - EvaluateRowsRequest - - GraphMemoryBank - - GraphMemoryBankParams - GreedySamplingStrategy - HealthInfo - ImageContentItem - ImageDelta - InferenceStep + - InsertChunksRequest - InsertDocumentsRequest - InterleavedContent - InterleavedContentItem @@ -6230,14 +6252,10 @@ x-tagGroups: - Job - JobStatus - JsonType - - KeyValueMemoryBank - - KeyValueMemoryBankParams - - KeywordMemoryBank - - KeywordMemoryBankParams - LLMAsJudgeScoringFnParams + - LLMRAGQueryGeneratorConfig - ListDatasetsResponse - ListEvalTasksResponse - - ListMemoryBanksResponse - ListModelsResponse - ListPostTrainingJobsResponse - ListProvidersResponse @@ -6246,11 +6264,10 @@ x-tagGroups: - ListShieldsResponse - ListToolGroupsResponse - ListToolsResponse + - ListVectorDBsResponse - LogEventRequest - LogSeverity - LoraFinetuningConfig - - MemoryBank - - MemoryBankDocument - MemoryRetrievalStep - Message - MetricEvent @@ -6269,21 +6286,26 @@ x-tagGroups: - PreferenceOptimizeRequest - ProviderInfo - QATFinetuningConfig + - QueryChunksRequest + - QueryChunksResponse - QueryCondition - QueryConditionOp - - QueryDocumentsRequest - - QueryDocumentsResponse + - QueryContextRequest - QuerySpanTreeResponse - QuerySpansResponse - QueryTracesResponse + - RAGDocument + - RAGQueryConfig + - RAGQueryGeneratorConfig + - RAGQueryResult - RegexParserScoringFnParams - RegisterDatasetRequest - RegisterEvalTaskRequest - - RegisterMemoryBankRequest - RegisterModelRequest - RegisterScoringFunctionRequest - RegisterShieldRequest - RegisterToolGroupRequest + - RegisterVectorDbRequest - ResponseFormat - RouteInfo - RunEvalRequest @@ -6341,7 +6363,6 @@ x-tagGroups: - UnionType - UnstructuredLogEvent - UserMessage - - VectorMemoryBank - - VectorMemoryBankParams + - VectorDB - VersionInfo - ViolationLevel diff --git a/llama_stack/apis/tools/__init__.py b/llama_stack/apis/tools/__init__.py index f747fcdc2..8cd798ebf 100644 --- a/llama_stack/apis/tools/__init__.py +++ b/llama_stack/apis/tools/__init__.py @@ -5,3 +5,4 @@ # the root directory of this source tree. from .tools import * # noqa: F401 F403 +from .rag_tool import * # noqa: F401 F403 diff --git a/llama_stack/apis/tools/rag_tool.py b/llama_stack/apis/tools/rag_tool.py new file mode 100644 index 000000000..0247bb384 --- /dev/null +++ b/llama_stack/apis/tools/rag_tool.py @@ -0,0 +1,95 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +from enum import Enum +from typing import Any, Dict, List, Literal, Optional, Union + +from llama_models.schema_utils import json_schema_type, register_schema, webmethod +from pydantic import BaseModel, Field +from typing_extensions import Annotated, Protocol, runtime_checkable + +from llama_stack.apis.common.content_types import InterleavedContent, URL +from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol + + +@json_schema_type +class RAGDocument(BaseModel): + document_id: str + content: InterleavedContent | URL + mime_type: str | None = None + metadata: Dict[str, Any] = Field(default_factory=dict) + + +@json_schema_type +class RAGQueryResult(BaseModel): + content: Optional[InterleavedContent] = None + + +@json_schema_type +class RAGQueryGenerator(Enum): + default = "default" + llm = "llm" + custom = "custom" + + +@json_schema_type +class DefaultRAGQueryGeneratorConfig(BaseModel): + type: Literal["default"] = "default" + separator: str = " " + + +@json_schema_type +class LLMRAGQueryGeneratorConfig(BaseModel): + type: Literal["llm"] = "llm" + model: str + template: str + + +RAGQueryGeneratorConfig = register_schema( + Annotated[ + Union[ + DefaultRAGQueryGeneratorConfig, + LLMRAGQueryGeneratorConfig, + ], + Field(discriminator="type"), + ], + name="RAGQueryGeneratorConfig", +) + + +@json_schema_type +class RAGQueryConfig(BaseModel): + # This config defines how a query is generated using the messages + # for memory bank retrieval. + query_generator_config: RAGQueryGeneratorConfig = Field( + default=DefaultRAGQueryGeneratorConfig() + ) + max_tokens_in_context: int = 4096 + max_chunks: int = 5 + + +@runtime_checkable +@trace_protocol +class RAGToolRuntime(Protocol): + @webmethod(route="/tool-runtime/rag-tool/insert-documents", method="POST") + async def insert_documents( + self, + documents: List[RAGDocument], + vector_db_id: str, + chunk_size_in_tokens: int = 512, + ) -> None: + """Index documents so they can be used by the RAG system""" + ... + + @webmethod(route="/tool-runtime/rag-tool/query-context", method="POST") + async def query_context( + self, + content: InterleavedContent, + query_config: RAGQueryConfig, + vector_db_ids: List[str], + ) -> RAGQueryResult: + """Query the RAG system for context; typically invoked by the agent""" + ... diff --git a/llama_stack/apis/tools/tools.py b/llama_stack/apis/tools/tools.py index fb990cc41..1af019bd4 100644 --- a/llama_stack/apis/tools/tools.py +++ b/llama_stack/apis/tools/tools.py @@ -15,6 +15,8 @@ from llama_stack.apis.common.content_types import InterleavedContent, URL from llama_stack.apis.resource import Resource, ResourceType from llama_stack.providers.utils.telemetry.trace_protocol import trace_protocol +from .rag_tool import RAGToolRuntime + @json_schema_type class ToolParameter(BaseModel): @@ -130,11 +132,17 @@ class ToolGroups(Protocol): ... +class SpecialToolGroup(Enum): + rag_tool = "rag_tool" + + @runtime_checkable @trace_protocol class ToolRuntime(Protocol): tool_store: ToolStore + rag_tool: RAGToolRuntime + # TODO: This needs to be renamed once OPEN API generator name conflict issue is fixed. @webmethod(route="/tool-runtime/list-tools", method="GET") async def list_runtime_tools( @@ -143,7 +151,7 @@ class ToolRuntime(Protocol): @webmethod(route="/tool-runtime/invoke", method="POST") async def invoke_tool( - self, tool_name: str, args: Dict[str, Any] + self, tool_name: str, kwargs: Dict[str, Any] ) -> ToolInvocationResult: """Run a tool with the given arguments""" ... diff --git a/llama_stack/distribution/resolver.py b/llama_stack/distribution/resolver.py index bd5a9ae98..dd6d4be6f 100644 --- a/llama_stack/distribution/resolver.py +++ b/llama_stack/distribution/resolver.py @@ -333,6 +333,8 @@ async def instantiate_provider( impl.__provider_spec__ = provider_spec impl.__provider_config__ = config + # TODO: check compliance for special tool groups + # the impl should be for Api.tool_runtime, the name should be the special tool group, the protocol should be the special tool group protocol check_protocol_compliance(impl, protocols[provider_spec.api]) if ( not isinstance(provider_spec, AutoRoutedProviderSpec) diff --git a/llama_stack/distribution/routers/routers.py b/llama_stack/distribution/routers/routers.py index 979c68b72..3ae9833dc 100644 --- a/llama_stack/distribution/routers/routers.py +++ b/llama_stack/distribution/routers/routers.py @@ -36,7 +36,14 @@ from llama_stack.apis.scoring import ( ScoringFnParams, ) from llama_stack.apis.shields import Shield -from llama_stack.apis.tools import ToolDef, ToolRuntime +from llama_stack.apis.tools import ( + RAGDocument, + RAGQueryConfig, + RAGQueryResult, + RAGToolRuntime, + ToolDef, + ToolRuntime, +) from llama_stack.apis.vector_io import Chunk, QueryChunksResponse, VectorIO from llama_stack.providers.datatypes import RoutingTable @@ -400,22 +407,55 @@ class EvalRouter(Eval): class ToolRuntimeRouter(ToolRuntime): + class RagToolImpl(RAGToolRuntime): + def __init__( + self, + routing_table: RoutingTable, + ) -> None: + self.routing_table = routing_table + + async def query_context( + self, + content: InterleavedContent, + query_config: RAGQueryConfig, + vector_db_ids: List[str], + ) -> RAGQueryResult: + return await self.routing_table.get_provider_impl( + "rag_tool.query_context" + ).query_context(content, query_config, vector_db_ids) + + async def insert_documents( + self, + documents: List[RAGDocument], + vector_db_id: str, + chunk_size_in_tokens: int = 512, + ) -> None: + return await self.routing_table.get_provider_impl( + "rag_tool.insert_documents" + ).insert_documents(documents, vector_db_id, chunk_size_in_tokens) + def __init__( self, routing_table: RoutingTable, ) -> None: self.routing_table = routing_table + # HACK ALERT this should be in sync with "get_all_api_endpoints()" + # TODO: make sure rag_tool vs builtin::memory is correct everywhere + self.rag_tool = self.RagToolImpl(routing_table) + setattr(self, "rag_tool.query_context", self.rag_tool.query_context) + setattr(self, "rag_tool.insert_documents", self.rag_tool.insert_documents) + async def initialize(self) -> None: pass async def shutdown(self) -> None: pass - async def invoke_tool(self, tool_name: str, args: Dict[str, Any]) -> Any: + async def invoke_tool(self, tool_name: str, kwargs: Dict[str, Any]) -> Any: return await self.routing_table.get_provider_impl(tool_name).invoke_tool( tool_name=tool_name, - args=args, + kwargs=kwargs, ) async def list_runtime_tools( diff --git a/llama_stack/distribution/server/endpoints.py b/llama_stack/distribution/server/endpoints.py index af429e020..180479e40 100644 --- a/llama_stack/distribution/server/endpoints.py +++ b/llama_stack/distribution/server/endpoints.py @@ -9,6 +9,8 @@ from typing import Dict, List from pydantic import BaseModel +from llama_stack.apis.tools import RAGToolRuntime, SpecialToolGroup + from llama_stack.apis.version import LLAMA_STACK_API_VERSION from llama_stack.distribution.resolver import api_protocol_map @@ -22,21 +24,39 @@ class ApiEndpoint(BaseModel): name: str +def toolgroup_protocol_map(): + return { + SpecialToolGroup.rag_tool: RAGToolRuntime, + } + + def get_all_api_endpoints() -> Dict[Api, List[ApiEndpoint]]: apis = {} protocols = api_protocol_map() + toolgroup_protocols = toolgroup_protocol_map() for api, protocol in protocols.items(): endpoints = [] protocol_methods = inspect.getmembers(protocol, predicate=inspect.isfunction) + # HACK ALERT + if api == Api.tool_runtime: + for tool_group in SpecialToolGroup: + sub_protocol = toolgroup_protocols[tool_group] + sub_protocol_methods = inspect.getmembers( + sub_protocol, predicate=inspect.isfunction + ) + for name, method in sub_protocol_methods: + if not hasattr(method, "__webmethod__"): + continue + protocol_methods.append((f"{tool_group.value}.{name}", method)) + for name, method in protocol_methods: if not hasattr(method, "__webmethod__"): continue webmethod = method.__webmethod__ route = f"/{LLAMA_STACK_API_VERSION}/{webmethod.route.lstrip('/')}" - if webmethod.method == "GET": method = "get" elif webmethod.method == "DELETE": diff --git a/llama_stack/distribution/stack.py b/llama_stack/distribution/stack.py index 180ec0ecc..f0c34dba4 100644 --- a/llama_stack/distribution/stack.py +++ b/llama_stack/distribution/stack.py @@ -29,7 +29,7 @@ from llama_stack.apis.scoring_functions import ScoringFunctions from llama_stack.apis.shields import Shields from llama_stack.apis.synthetic_data_generation import SyntheticDataGeneration from llama_stack.apis.telemetry import Telemetry -from llama_stack.apis.tools import ToolGroups, ToolRuntime +from llama_stack.apis.tools import RAGToolRuntime, ToolGroups, ToolRuntime from llama_stack.apis.vector_dbs import VectorDBs from llama_stack.apis.vector_io import VectorIO from llama_stack.distribution.datatypes import StackRunConfig @@ -62,6 +62,7 @@ class LlamaStack( Inspect, ToolGroups, ToolRuntime, + RAGToolRuntime, ): pass diff --git a/llama_stack/distribution/store/registry.py b/llama_stack/distribution/store/registry.py index 010d137ec..5c0b8b5db 100644 --- a/llama_stack/distribution/store/registry.py +++ b/llama_stack/distribution/store/registry.py @@ -35,7 +35,7 @@ class DistributionRegistry(Protocol): REGISTER_PREFIX = "distributions:registry" -KEY_VERSION = "v5" +KEY_VERSION = "v6" KEY_FORMAT = f"{REGISTER_PREFIX}:{KEY_VERSION}::" + "{type}:{identifier}" diff --git a/llama_stack/providers/inline/agents/meta_reference/__init__.py b/llama_stack/providers/inline/agents/meta_reference/__init__.py index 50f61fb42..de34b8d2c 100644 --- a/llama_stack/providers/inline/agents/meta_reference/__init__.py +++ b/llama_stack/providers/inline/agents/meta_reference/__init__.py @@ -19,9 +19,8 @@ async def get_provider_impl( impl = MetaReferenceAgentsImpl( config, deps[Api.inference], - deps[Api.memory], + deps[Api.vector_io], deps[Api.safety], - deps[Api.memory_banks], deps[Api.tool_runtime], deps[Api.tool_groups], ) diff --git a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py index 2ebc7ded1..5b5175cee 100644 --- a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py +++ b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py @@ -59,13 +59,18 @@ from llama_stack.apis.inference import ( ToolResponseMessage, UserMessage, ) -from llama_stack.apis.memory import Memory, MemoryBankDocument -from llama_stack.apis.memory_banks import MemoryBanks, VectorMemoryBankParams from llama_stack.apis.safety import Safety -from llama_stack.apis.tools import ToolGroups, ToolRuntime +from llama_stack.apis.tools import ( + DefaultRAGQueryGeneratorConfig, + RAGDocument, + RAGQueryConfig, + ToolGroups, + ToolRuntime, +) +from llama_stack.apis.vector_io import VectorIO from llama_stack.providers.utils.kvstore import KVStore +from llama_stack.providers.utils.memory.vector_store import concat_interleaved_content from llama_stack.providers.utils.telemetry import tracing - from .persistence import AgentPersistence from .safety import SafetyException, ShieldRunnerMixin @@ -79,7 +84,7 @@ def make_random_string(length: int = 8): TOOLS_ATTACHMENT_KEY_REGEX = re.compile(r"__tools_attachment__=(\{.*?\})") -MEMORY_QUERY_TOOL = "query_memory" +MEMORY_QUERY_TOOL = "rag_tool.query_context" WEB_SEARCH_TOOL = "web_search" MEMORY_GROUP = "builtin::memory" @@ -91,20 +96,18 @@ class ChatAgent(ShieldRunnerMixin): agent_config: AgentConfig, tempdir: str, inference_api: Inference, - memory_api: Memory, - memory_banks_api: MemoryBanks, safety_api: Safety, tool_runtime_api: ToolRuntime, tool_groups_api: ToolGroups, + vector_io_api: VectorIO, persistence_store: KVStore, ): self.agent_id = agent_id self.agent_config = agent_config self.tempdir = tempdir self.inference_api = inference_api - self.memory_api = memory_api - self.memory_banks_api = memory_banks_api self.safety_api = safety_api + self.vector_io_api = vector_io_api self.storage = AgentPersistence(agent_id, persistence_store) self.tool_runtime_api = tool_runtime_api self.tool_groups_api = tool_groups_api @@ -370,24 +373,30 @@ class ChatAgent(ShieldRunnerMixin): documents: Optional[List[Document]] = None, toolgroups_for_turn: Optional[List[AgentToolGroup]] = None, ) -> AsyncGenerator: + # TODO: simplify all of this code, it can be simpler toolgroup_args = {} + toolgroups = set() for toolgroup in self.agent_config.toolgroups: if isinstance(toolgroup, AgentToolGroupWithArgs): + toolgroups.add(toolgroup.name) toolgroup_args[toolgroup.name] = toolgroup.args + else: + toolgroups.add(toolgroup) if toolgroups_for_turn: for toolgroup in toolgroups_for_turn: if isinstance(toolgroup, AgentToolGroupWithArgs): + toolgroups.add(toolgroup.name) toolgroup_args[toolgroup.name] = toolgroup.args + else: + toolgroups.add(toolgroup) tool_defs, tool_to_group = await self._get_tool_defs(toolgroups_for_turn) if documents: await self.handle_documents( session_id, documents, input_messages, tool_defs ) - if MEMORY_QUERY_TOOL in tool_defs and len(input_messages) > 0: - memory_tool_group = tool_to_group.get(MEMORY_QUERY_TOOL, None) - if memory_tool_group is None: - raise ValueError(f"Memory tool group not found for {MEMORY_QUERY_TOOL}") + + if MEMORY_GROUP in toolgroups and len(input_messages) > 0: with tracing.span(MEMORY_QUERY_TOOL) as span: step_id = str(uuid.uuid4()) yield AgentTurnResponseStreamChunk( @@ -398,17 +407,15 @@ class ChatAgent(ShieldRunnerMixin): ) ) ) - query_args = { - "messages": [msg.content for msg in input_messages], - **toolgroup_args.get(memory_tool_group, {}), - } + args = toolgroup_args.get(MEMORY_GROUP, {}) + vector_db_ids = args.get("vector_db_ids", []) session_info = await self.storage.get_session_info(session_id) + # if the session has a memory bank id, let the memory tool use it if session_info.memory_bank_id: - if "memory_bank_ids" not in query_args: - query_args["memory_bank_ids"] = [] - query_args["memory_bank_ids"].append(session_info.memory_bank_id) + vector_db_ids.append(session_info.memory_bank_id) + yield AgentTurnResponseStreamChunk( event=AgentTurnResponseEvent( payload=AgentTurnResponseStepProgressPayload( @@ -425,10 +432,18 @@ class ChatAgent(ShieldRunnerMixin): ) ) ) - result = await self.tool_runtime_api.invoke_tool( - tool_name=MEMORY_QUERY_TOOL, - args=query_args, + result = await self.tool_runtime_api.rag_tool.query_context( + content=concat_interleaved_content( + [msg.content for msg in input_messages] + ), + query_config=RAGQueryConfig( + query_generator_config=DefaultRAGQueryGeneratorConfig(), + max_tokens_in_context=4096, + max_chunks=5, + ), + vector_db_ids=vector_db_ids, ) + retrieved_context = result.content yield AgentTurnResponseStreamChunk( event=AgentTurnResponseEvent( @@ -449,7 +464,7 @@ class ChatAgent(ShieldRunnerMixin): ToolResponse( call_id="", tool_name=MEMORY_QUERY_TOOL, - content=result.content, + content=retrieved_context or [], ) ], ), @@ -459,13 +474,11 @@ class ChatAgent(ShieldRunnerMixin): span.set_attribute( "input", [m.model_dump_json() for m in input_messages] ) - span.set_attribute("output", result.content) - span.set_attribute("error_code", result.error_code) - span.set_attribute("error_message", result.error_message) + span.set_attribute("output", retrieved_context) span.set_attribute("tool_name", MEMORY_QUERY_TOOL) - if result.error_code == 0: + if retrieved_context: last_message = input_messages[-1] - last_message.context = result.content + last_message.context = retrieved_context output_attachments = [] @@ -842,12 +855,13 @@ class ChatAgent(ShieldRunnerMixin): if session_info.memory_bank_id is None: bank_id = f"memory_bank_{session_id}" - await self.memory_banks_api.register_memory_bank( - memory_bank_id=bank_id, - params=VectorMemoryBankParams( - embedding_model="all-MiniLM-L6-v2", - chunk_size_in_tokens=512, - ), + + # TODO: the semantic for registration is definitely not "creation" + # so we need to fix it if we expect the agent to create a new vector db + # for each session + await self.vector_io_api.register_vector_db( + vector_db_id=bank_id, + embedding_model="all-MiniLM-L6-v2", ) await self.storage.add_memory_bank_to_session(session_id, bank_id) else: @@ -858,9 +872,9 @@ class ChatAgent(ShieldRunnerMixin): async def add_to_session_memory_bank( self, session_id: str, data: List[Document] ) -> None: - bank_id = await self._ensure_memory_bank(session_id) + vector_db_id = await self._ensure_memory_bank(session_id) documents = [ - MemoryBankDocument( + RAGDocument( document_id=str(uuid.uuid4()), content=a.content, mime_type=a.mime_type, @@ -868,9 +882,10 @@ class ChatAgent(ShieldRunnerMixin): ) for a in data ] - await self.memory_api.insert_documents( - bank_id=bank_id, + await self.tool_runtime_api.rag_tool.insert_documents( documents=documents, + vector_db_id=vector_db_id, + chunk_size_in_tokens=512, ) @@ -955,7 +970,7 @@ async def execute_tool_call_maybe( result = await tool_runtime_api.invoke_tool( tool_name=name, - args=dict( + kwargs=dict( session_id=session_id, **tool_call_args, ), diff --git a/llama_stack/providers/inline/agents/meta_reference/agents.py b/llama_stack/providers/inline/agents/meta_reference/agents.py index d22ef82ab..b1844f4d0 100644 --- a/llama_stack/providers/inline/agents/meta_reference/agents.py +++ b/llama_stack/providers/inline/agents/meta_reference/agents.py @@ -26,10 +26,9 @@ from llama_stack.apis.agents import ( Turn, ) from llama_stack.apis.inference import Inference, ToolResponseMessage, UserMessage -from llama_stack.apis.memory import Memory -from llama_stack.apis.memory_banks import MemoryBanks from llama_stack.apis.safety import Safety from llama_stack.apis.tools import ToolGroups, ToolRuntime +from llama_stack.apis.vector_io import VectorIO from llama_stack.providers.utils.kvstore import InmemoryKVStoreImpl, kvstore_impl from .agent_instance import ChatAgent @@ -44,17 +43,15 @@ class MetaReferenceAgentsImpl(Agents): self, config: MetaReferenceAgentsImplConfig, inference_api: Inference, - memory_api: Memory, + vector_io_api: VectorIO, safety_api: Safety, - memory_banks_api: MemoryBanks, tool_runtime_api: ToolRuntime, tool_groups_api: ToolGroups, ): self.config = config self.inference_api = inference_api - self.memory_api = memory_api + self.vector_io_api = vector_io_api self.safety_api = safety_api - self.memory_banks_api = memory_banks_api self.tool_runtime_api = tool_runtime_api self.tool_groups_api = tool_groups_api @@ -114,8 +111,7 @@ class MetaReferenceAgentsImpl(Agents): tempdir=self.tempdir, inference_api=self.inference_api, safety_api=self.safety_api, - memory_api=self.memory_api, - memory_banks_api=self.memory_banks_api, + vector_io_api=self.vector_io_api, tool_runtime_api=self.tool_runtime_api, tool_groups_api=self.tool_groups_api, persistence_store=( diff --git a/llama_stack/providers/inline/tool_runtime/code_interpreter/code_interpreter.py b/llama_stack/providers/inline/tool_runtime/code_interpreter/code_interpreter.py index 361c91a92..04434768d 100644 --- a/llama_stack/providers/inline/tool_runtime/code_interpreter/code_interpreter.py +++ b/llama_stack/providers/inline/tool_runtime/code_interpreter/code_interpreter.py @@ -60,9 +60,9 @@ class CodeInterpreterToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime): ] async def invoke_tool( - self, tool_name: str, args: Dict[str, Any] + self, tool_name: str, kwargs: Dict[str, Any] ) -> ToolInvocationResult: - script = args["code"] + script = kwargs["code"] req = CodeExecutionRequest(scripts=[script]) res = self.code_executor.execute(req) pieces = [res["process_status"]] diff --git a/llama_stack/providers/inline/tool_runtime/memory/__init__.py b/llama_stack/providers/inline/tool_runtime/memory/__init__.py index 928afa484..42a0a6b01 100644 --- a/llama_stack/providers/inline/tool_runtime/memory/__init__.py +++ b/llama_stack/providers/inline/tool_runtime/memory/__init__.py @@ -13,8 +13,6 @@ from .memory import MemoryToolRuntimeImpl async def get_provider_impl(config: MemoryToolRuntimeConfig, deps: Dict[str, Any]): - impl = MemoryToolRuntimeImpl( - config, deps[Api.memory], deps[Api.memory_banks], deps[Api.inference] - ) + impl = MemoryToolRuntimeImpl(config, deps[Api.vector_io], deps[Api.inference]) await impl.initialize() return impl diff --git a/llama_stack/providers/inline/tool_runtime/memory/config.py b/llama_stack/providers/inline/tool_runtime/memory/config.py index 6ff242c6b..4a20c986c 100644 --- a/llama_stack/providers/inline/tool_runtime/memory/config.py +++ b/llama_stack/providers/inline/tool_runtime/memory/config.py @@ -4,87 +4,8 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -from enum import Enum -from typing import Annotated, List, Literal, Union - -from pydantic import BaseModel, Field - - -class _MemoryBankConfigCommon(BaseModel): - bank_id: str - - -class VectorMemoryBankConfig(_MemoryBankConfigCommon): - type: Literal["vector"] = "vector" - - -class KeyValueMemoryBankConfig(_MemoryBankConfigCommon): - type: Literal["keyvalue"] = "keyvalue" - keys: List[str] # what keys to focus on - - -class KeywordMemoryBankConfig(_MemoryBankConfigCommon): - type: Literal["keyword"] = "keyword" - - -class GraphMemoryBankConfig(_MemoryBankConfigCommon): - type: Literal["graph"] = "graph" - entities: List[str] # what entities to focus on - - -MemoryBankConfig = Annotated[ - Union[ - VectorMemoryBankConfig, - KeyValueMemoryBankConfig, - KeywordMemoryBankConfig, - GraphMemoryBankConfig, - ], - Field(discriminator="type"), -] - - -class MemoryQueryGenerator(Enum): - default = "default" - llm = "llm" - custom = "custom" - - -class DefaultMemoryQueryGeneratorConfig(BaseModel): - type: Literal[MemoryQueryGenerator.default.value] = ( - MemoryQueryGenerator.default.value - ) - sep: str = " " - - -class LLMMemoryQueryGeneratorConfig(BaseModel): - type: Literal[MemoryQueryGenerator.llm.value] = MemoryQueryGenerator.llm.value - model: str - template: str - - -class CustomMemoryQueryGeneratorConfig(BaseModel): - type: Literal[MemoryQueryGenerator.custom.value] = MemoryQueryGenerator.custom.value - - -MemoryQueryGeneratorConfig = Annotated[ - Union[ - DefaultMemoryQueryGeneratorConfig, - LLMMemoryQueryGeneratorConfig, - CustomMemoryQueryGeneratorConfig, - ], - Field(discriminator="type"), -] - - -class MemoryToolConfig(BaseModel): - memory_bank_configs: List[MemoryBankConfig] = Field(default_factory=list) +from pydantic import BaseModel class MemoryToolRuntimeConfig(BaseModel): - # This config defines how a query is generated using the messages - # for memory bank retrieval. - query_generator_config: MemoryQueryGeneratorConfig = Field( - default=DefaultMemoryQueryGeneratorConfig() - ) - max_tokens_in_context: int = 4096 - max_chunks: int = 5 + pass diff --git a/llama_stack/providers/inline/tool_runtime/memory/context_retriever.py b/llama_stack/providers/inline/tool_runtime/memory/context_retriever.py index 803981f07..e77ec76af 100644 --- a/llama_stack/providers/inline/tool_runtime/memory/context_retriever.py +++ b/llama_stack/providers/inline/tool_runtime/memory/context_retriever.py @@ -5,68 +5,64 @@ # the root directory of this source tree. -from typing import List - from jinja2 import Template -from pydantic import BaseModel from llama_stack.apis.common.content_types import InterleavedContent from llama_stack.apis.inference import UserMessage + +from llama_stack.apis.tools.rag_tool import ( + DefaultRAGQueryGeneratorConfig, + LLMRAGQueryGeneratorConfig, + RAGQueryGenerator, + RAGQueryGeneratorConfig, +) from llama_stack.providers.utils.inference.prompt_adapter import ( interleaved_content_as_str, ) -from .config import ( - DefaultMemoryQueryGeneratorConfig, - LLMMemoryQueryGeneratorConfig, - MemoryQueryGenerator, - MemoryQueryGeneratorConfig, -) - async def generate_rag_query( - config: MemoryQueryGeneratorConfig, - messages: List[InterleavedContent], + config: RAGQueryGeneratorConfig, + content: InterleavedContent, **kwargs, ): """ Generates a query that will be used for retrieving relevant information from the memory bank. """ - if config.type == MemoryQueryGenerator.default.value: - query = await default_rag_query_generator(config, messages, **kwargs) - elif config.type == MemoryQueryGenerator.llm.value: - query = await llm_rag_query_generator(config, messages, **kwargs) + if config.type == RAGQueryGenerator.default.value: + query = await default_rag_query_generator(config, content, **kwargs) + elif config.type == RAGQueryGenerator.llm.value: + query = await llm_rag_query_generator(config, content, **kwargs) else: raise NotImplementedError(f"Unsupported memory query generator {config.type}") return query async def default_rag_query_generator( - config: DefaultMemoryQueryGeneratorConfig, - messages: List[InterleavedContent], + config: DefaultRAGQueryGeneratorConfig, + content: InterleavedContent, **kwargs, ): - return config.sep.join(interleaved_content_as_str(m) for m in messages) + return interleaved_content_as_str(content, sep=config.separator) async def llm_rag_query_generator( - config: LLMMemoryQueryGeneratorConfig, - messages: List[InterleavedContent], + config: LLMRAGQueryGeneratorConfig, + content: InterleavedContent, **kwargs, ): assert "inference_api" in kwargs, "LLMRAGQueryGenerator needs inference_api" inference_api = kwargs["inference_api"] - m_dict = { - "messages": [ - message.model_dump() if isinstance(message, BaseModel) else message - for message in messages - ] - } + messages = [] + if isinstance(content, list): + messages = [interleaved_content_as_str(m) for m in content] + else: + messages = [interleaved_content_as_str(content)] template = Template(config.template) - content = template.render(m_dict) + content = template.render({"messages": messages}) model = config.model message = UserMessage(content=content) diff --git a/llama_stack/providers/inline/tool_runtime/memory/memory.py b/llama_stack/providers/inline/tool_runtime/memory/memory.py index fe6325abb..d3f8b07dc 100644 --- a/llama_stack/providers/inline/tool_runtime/memory/memory.py +++ b/llama_stack/providers/inline/tool_runtime/memory/memory.py @@ -10,20 +10,29 @@ import secrets import string from typing import Any, Dict, List, Optional -from llama_stack.apis.common.content_types import URL -from llama_stack.apis.inference import Inference, InterleavedContent -from llama_stack.apis.memory import Memory, QueryDocumentsResponse -from llama_stack.apis.memory_banks import MemoryBanks +from llama_stack.apis.common.content_types import ( + InterleavedContent, + TextContentItem, + URL, +) +from llama_stack.apis.inference import Inference from llama_stack.apis.tools import ( + RAGDocument, + RAGQueryConfig, + RAGQueryResult, + RAGToolRuntime, ToolDef, ToolInvocationResult, - ToolParameter, ToolRuntime, ) +from llama_stack.apis.vector_io import QueryChunksResponse, VectorIO from llama_stack.providers.datatypes import ToolsProtocolPrivate -from llama_stack.providers.utils.memory.vector_store import concat_interleaved_content +from llama_stack.providers.utils.memory.vector_store import ( + content_from_doc, + make_overlapped_chunks, +) -from .config import MemoryToolConfig, MemoryToolRuntimeConfig +from .config import MemoryToolRuntimeConfig from .context_retriever import generate_rag_query log = logging.getLogger(__name__) @@ -35,65 +44,79 @@ def make_random_string(length: int = 8): ) -class MemoryToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime): +class MemoryToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, RAGToolRuntime): def __init__( self, config: MemoryToolRuntimeConfig, - memory_api: Memory, - memory_banks_api: MemoryBanks, + vector_io_api: VectorIO, inference_api: Inference, ): self.config = config - self.memory_api = memory_api - self.memory_banks_api = memory_banks_api + self.vector_io_api = vector_io_api self.inference_api = inference_api async def initialize(self): pass - async def list_runtime_tools( - self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None - ) -> List[ToolDef]: - return [ - ToolDef( - name="query_memory", - description="Retrieve context from memory", - parameters=[ - ToolParameter( - name="messages", - description="The input messages to search for", - parameter_type="array", - ), - ], - ) - ] + async def shutdown(self): + pass + + async def insert_documents( + self, + documents: List[RAGDocument], + vector_db_id: str, + chunk_size_in_tokens: int = 512, + ) -> None: + chunks = [] + for doc in documents: + content = await content_from_doc(doc) + chunks.extend( + make_overlapped_chunks( + doc.document_id, + content, + chunk_size_in_tokens, + chunk_size_in_tokens // 4, + ) + ) + + if not chunks: + return + + await self.vector_io_api.insert_chunks( + chunks=chunks, + vector_db_id=vector_db_id, + ) + + async def query_context( + self, + content: InterleavedContent, + query_config: RAGQueryConfig, + vector_db_ids: List[str], + ) -> RAGQueryResult: + if not vector_db_ids: + return RAGQueryResult(content=None) - async def _retrieve_context( - self, input_messages: List[InterleavedContent], bank_ids: List[str] - ) -> Optional[List[InterleavedContent]]: - if not bank_ids: - return None query = await generate_rag_query( - self.config.query_generator_config, - input_messages, + query_config.query_generator_config, + content, inference_api=self.inference_api, ) tasks = [ - self.memory_api.query_documents( - bank_id=bank_id, + self.vector_io_api.query_chunks( + vector_db_id=vector_db_id, query=query, params={ - "max_chunks": self.config.max_chunks, + "max_chunks": query_config.max_chunks, }, ) - for bank_id in bank_ids + for vector_db_id in vector_db_ids ] - results: List[QueryDocumentsResponse] = await asyncio.gather(*tasks) + results: List[QueryChunksResponse] = await asyncio.gather(*tasks) chunks = [c for r in results for c in r.chunks] scores = [s for r in results for s in r.scores] if not chunks: - return None + return RAGQueryResult(content=None) # sort by score chunks, scores = zip( @@ -102,45 +125,52 @@ class MemoryToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime): tokens = 0 picked = [] - for c in chunks[: self.config.max_chunks]: - tokens += c.token_count - if tokens > self.config.max_tokens_in_context: + for c in chunks[: query_config.max_chunks]: + metadata = c.metadata + tokens += metadata["token_count"] + if tokens > query_config.max_tokens_in_context: log.error( f"Using {len(picked)} chunks; reached max tokens in context: {tokens}", ) break - picked.append(f"id:{c.document_id}; content:{c.content}") + picked.append( + TextContentItem( + text=f"id:{metadata['document_id']}; content:{c.content}", + ) + ) + return RAGQueryResult( + content=[ + TextContentItem( + text="Here are the retrieved documents for relevant context:\n=== START-RETRIEVED-CONTEXT ===\n", + ), + *picked, + TextContentItem( + text="\n=== END-RETRIEVED-CONTEXT ===\n", + ), + ], + ) + + async def list_runtime_tools( + self, tool_group_id: Optional[str] = None, mcp_endpoint: Optional[URL] = None + ) -> List[ToolDef]: + # Parameters are not listed since these methods are not yet invoked automatically + # by the LLM. The method is only implemented so things like /tools can list without + # encountering fatals. return [ - "Here are the retrieved documents for relevant context:\n=== START-RETRIEVED-CONTEXT ===\n", - *picked, - "\n=== END-RETRIEVED-CONTEXT ===\n", + ToolDef( + name="rag_tool.query_context", + description="Retrieve context from memory", + ), + ToolDef( + name="rag_tool.insert_documents", + description="Insert documents into memory", + ), ] async def invoke_tool( - self, tool_name: str, args: Dict[str, Any] + self, tool_name: str, kwargs: Dict[str, Any] ) -> ToolInvocationResult: - tool = await self.tool_store.get_tool(tool_name) - tool_group = await self.tool_store.get_tool_group(tool.toolgroup_id) - final_args = tool_group.args or {} - final_args.update(args) - config = MemoryToolConfig() - if tool.metadata and tool.metadata.get("config") is not None: - config = MemoryToolConfig(**tool.metadata["config"]) - if "memory_bank_ids" in final_args: - bank_ids = final_args["memory_bank_ids"] - else: - bank_ids = [ - bank_config.bank_id for bank_config in config.memory_bank_configs - ] - if "messages" not in final_args: - raise ValueError("messages are required") - context = await self._retrieve_context( - final_args["messages"], - bank_ids, - ) - if context is None: - context = [] - return ToolInvocationResult( - content=concat_interleaved_content(context), error_code=0 + raise RuntimeError( + "This toolgroup should not be called generically but only through specific methods of the RAGToolRuntime protocol" ) diff --git a/llama_stack/providers/registry/tool_runtime.py b/llama_stack/providers/registry/tool_runtime.py index b3ea68949..426fe22f2 100644 --- a/llama_stack/providers/registry/tool_runtime.py +++ b/llama_stack/providers/registry/tool_runtime.py @@ -23,7 +23,7 @@ def available_providers() -> List[ProviderSpec]: pip_packages=[], module="llama_stack.providers.inline.tool_runtime.memory", config_class="llama_stack.providers.inline.tool_runtime.memory.config.MemoryToolRuntimeConfig", - api_dependencies=[Api.vector_io, Api.vector_dbs, Api.inference], + api_dependencies=[Api.vector_io, Api.inference], ), InlineProviderSpec( api=Api.tool_runtime, diff --git a/llama_stack/providers/remote/tool_runtime/bing_search/bing_search.py b/llama_stack/providers/remote/tool_runtime/bing_search/bing_search.py index 5114e06aa..677e29c12 100644 --- a/llama_stack/providers/remote/tool_runtime/bing_search/bing_search.py +++ b/llama_stack/providers/remote/tool_runtime/bing_search/bing_search.py @@ -68,7 +68,7 @@ class BingSearchToolRuntimeImpl( ] async def invoke_tool( - self, tool_name: str, args: Dict[str, Any] + self, tool_name: str, kwargs: Dict[str, Any] ) -> ToolInvocationResult: api_key = self._get_api_key() headers = { @@ -78,7 +78,7 @@ class BingSearchToolRuntimeImpl( "count": self.config.top_k, "textDecorations": True, "textFormat": "HTML", - "q": args["query"], + "q": kwargs["query"], } response = requests.get( diff --git a/llama_stack/providers/remote/tool_runtime/brave_search/brave_search.py b/llama_stack/providers/remote/tool_runtime/brave_search/brave_search.py index 016f746ea..1162cc900 100644 --- a/llama_stack/providers/remote/tool_runtime/brave_search/brave_search.py +++ b/llama_stack/providers/remote/tool_runtime/brave_search/brave_search.py @@ -68,7 +68,7 @@ class BraveSearchToolRuntimeImpl( ] async def invoke_tool( - self, tool_name: str, args: Dict[str, Any] + self, tool_name: str, kwargs: Dict[str, Any] ) -> ToolInvocationResult: api_key = self._get_api_key() url = "https://api.search.brave.com/res/v1/web/search" @@ -77,7 +77,7 @@ class BraveSearchToolRuntimeImpl( "Accept-Encoding": "gzip", "Accept": "application/json", } - payload = {"q": args["query"]} + payload = {"q": kwargs["query"]} response = requests.get(url=url, params=payload, headers=headers) response.raise_for_status() results = self._clean_brave_response(response.json()) diff --git a/llama_stack/providers/remote/tool_runtime/model_context_protocol/model_context_protocol.py b/llama_stack/providers/remote/tool_runtime/model_context_protocol/model_context_protocol.py index a304167e9..e0caec1d0 100644 --- a/llama_stack/providers/remote/tool_runtime/model_context_protocol/model_context_protocol.py +++ b/llama_stack/providers/remote/tool_runtime/model_context_protocol/model_context_protocol.py @@ -65,7 +65,7 @@ class ModelContextProtocolToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime): return tools async def invoke_tool( - self, tool_name: str, args: Dict[str, Any] + self, tool_name: str, kwargs: Dict[str, Any] ) -> ToolInvocationResult: tool = await self.tool_store.get_tool(tool_name) if tool.metadata is None or tool.metadata.get("endpoint") is None: @@ -77,7 +77,7 @@ class ModelContextProtocolToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime): async with sse_client(endpoint) as streams: async with ClientSession(*streams) as session: await session.initialize() - result = await session.call_tool(tool.identifier, args) + result = await session.call_tool(tool.identifier, kwargs) return ToolInvocationResult( content="\n".join([result.model_dump_json() for result in result.content]), diff --git a/llama_stack/providers/remote/tool_runtime/tavily_search/tavily_search.py b/llama_stack/providers/remote/tool_runtime/tavily_search/tavily_search.py index 82077193e..f5826c0ff 100644 --- a/llama_stack/providers/remote/tool_runtime/tavily_search/tavily_search.py +++ b/llama_stack/providers/remote/tool_runtime/tavily_search/tavily_search.py @@ -67,12 +67,12 @@ class TavilySearchToolRuntimeImpl( ] async def invoke_tool( - self, tool_name: str, args: Dict[str, Any] + self, tool_name: str, kwargs: Dict[str, Any] ) -> ToolInvocationResult: api_key = self._get_api_key() response = requests.post( "https://api.tavily.com/search", - json={"api_key": api_key, "query": args["query"]}, + json={"api_key": api_key, "query": kwargs["query"]}, ) return ToolInvocationResult( diff --git a/llama_stack/providers/remote/tool_runtime/wolfram_alpha/wolfram_alpha.py b/llama_stack/providers/remote/tool_runtime/wolfram_alpha/wolfram_alpha.py index 04ecfcc15..bf298c13e 100644 --- a/llama_stack/providers/remote/tool_runtime/wolfram_alpha/wolfram_alpha.py +++ b/llama_stack/providers/remote/tool_runtime/wolfram_alpha/wolfram_alpha.py @@ -68,11 +68,11 @@ class WolframAlphaToolRuntimeImpl( ] async def invoke_tool( - self, tool_name: str, args: Dict[str, Any] + self, tool_name: str, kwargs: Dict[str, Any] ) -> ToolInvocationResult: api_key = self._get_api_key() params = { - "input": args["query"], + "input": kwargs["query"], "appid": api_key, "format": "plaintext", "output": "json", diff --git a/llama_stack/providers/tests/agents/conftest.py b/llama_stack/providers/tests/agents/conftest.py index 4efdfe8b7..9c115e3a1 100644 --- a/llama_stack/providers/tests/agents/conftest.py +++ b/llama_stack/providers/tests/agents/conftest.py @@ -12,10 +12,10 @@ from ..conftest import ( get_test_config_for_api, ) from ..inference.fixtures import INFERENCE_FIXTURES -from ..memory.fixtures import MEMORY_FIXTURES from ..safety.fixtures import SAFETY_FIXTURES, safety_model_from_shield from ..tools.fixtures import TOOL_RUNTIME_FIXTURES +from ..vector_io.fixtures import VECTOR_IO_FIXTURES from .fixtures import AGENTS_FIXTURES DEFAULT_PROVIDER_COMBINATIONS = [ @@ -23,7 +23,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ { "inference": "meta_reference", "safety": "llama_guard", - "memory": "faiss", + "vector_io": "faiss", "agents": "meta_reference", "tool_runtime": "memory_and_search", }, @@ -34,7 +34,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ { "inference": "ollama", "safety": "llama_guard", - "memory": "faiss", + "vector_io": "faiss", "agents": "meta_reference", "tool_runtime": "memory_and_search", }, @@ -46,7 +46,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ "inference": "together", "safety": "llama_guard", # make this work with Weaviate which is what the together distro supports - "memory": "faiss", + "vector_io": "faiss", "agents": "meta_reference", "tool_runtime": "memory_and_search", }, @@ -57,7 +57,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ { "inference": "fireworks", "safety": "llama_guard", - "memory": "faiss", + "vector_io": "faiss", "agents": "meta_reference", "tool_runtime": "memory_and_search", }, @@ -68,7 +68,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [ { "inference": "remote", "safety": "remote", - "memory": "remote", + "vector_io": "remote", "agents": "remote", "tool_runtime": "memory_and_search", }, @@ -115,7 +115,7 @@ def pytest_generate_tests(metafunc): available_fixtures = { "inference": INFERENCE_FIXTURES, "safety": SAFETY_FIXTURES, - "memory": MEMORY_FIXTURES, + "vector_io": VECTOR_IO_FIXTURES, "agents": AGENTS_FIXTURES, "tool_runtime": TOOL_RUNTIME_FIXTURES, } diff --git a/llama_stack/providers/tests/agents/fixtures.py b/llama_stack/providers/tests/agents/fixtures.py index 1b1781f36..bb4a6e6a3 100644 --- a/llama_stack/providers/tests/agents/fixtures.py +++ b/llama_stack/providers/tests/agents/fixtures.py @@ -69,7 +69,7 @@ async def agents_stack( providers = {} provider_data = {} - for key in ["inference", "safety", "memory", "agents", "tool_runtime"]: + for key in ["inference", "safety", "vector_io", "agents", "tool_runtime"]: fixture = request.getfixturevalue(f"{key}_{fixture_dict[key]}") providers[key] = fixture.providers if key == "inference": @@ -118,7 +118,7 @@ async def agents_stack( ) test_stack = await construct_stack_for_test( - [Api.agents, Api.inference, Api.safety, Api.memory, Api.tool_runtime], + [Api.agents, Api.inference, Api.safety, Api.vector_io, Api.tool_runtime], providers, provider_data, models=models, diff --git a/llama_stack/providers/tests/agents/test_agents.py b/llama_stack/providers/tests/agents/test_agents.py index 320096826..f11aef3ec 100644 --- a/llama_stack/providers/tests/agents/test_agents.py +++ b/llama_stack/providers/tests/agents/test_agents.py @@ -214,9 +214,11 @@ class TestAgents: turn_response = [ chunk async for chunk in await agents_impl.create_agent_turn(**turn_request) ] - assert len(turn_response) > 0 + # FIXME: we need to check the content of the turn response and ensure + # RAG actually worked + @pytest.mark.asyncio async def test_create_agent_turn_with_tavily_search( self, agents_stack, search_query_messages, common_params diff --git a/llama_stack/providers/tests/vector_io/test_vector_io.py b/llama_stack/providers/tests/vector_io/test_vector_io.py index 901b8bd11..521131f63 100644 --- a/llama_stack/providers/tests/vector_io/test_vector_io.py +++ b/llama_stack/providers/tests/vector_io/test_vector_io.py @@ -8,13 +8,12 @@ import uuid import pytest +from llama_stack.apis.tools import RAGDocument + from llama_stack.apis.vector_dbs import ListVectorDBsResponse, VectorDB from llama_stack.apis.vector_io import QueryChunksResponse -from llama_stack.providers.utils.memory.vector_store import ( - make_overlapped_chunks, - MemoryBankDocument, -) +from llama_stack.providers.utils.memory.vector_store import make_overlapped_chunks # How to run this test: # @@ -26,22 +25,22 @@ from llama_stack.providers.utils.memory.vector_store import ( @pytest.fixture(scope="session") def sample_chunks(): docs = [ - MemoryBankDocument( + RAGDocument( document_id="doc1", content="Python is a high-level programming language.", metadata={"category": "programming", "difficulty": "beginner"}, ), - MemoryBankDocument( + RAGDocument( document_id="doc2", content="Machine learning is a subset of artificial intelligence.", metadata={"category": "AI", "difficulty": "advanced"}, ), - MemoryBankDocument( + RAGDocument( document_id="doc3", content="Data structures are fundamental to computer science.", metadata={"category": "computer science", "difficulty": "intermediate"}, ), - MemoryBankDocument( + RAGDocument( document_id="doc4", content="Neural networks are inspired by biological neural networks.", metadata={"category": "AI", "difficulty": "advanced"}, diff --git a/llama_stack/providers/utils/memory/vector_store.py b/llama_stack/providers/utils/memory/vector_store.py index c2de6c714..82c0c9c07 100644 --- a/llama_stack/providers/utils/memory/vector_store.py +++ b/llama_stack/providers/utils/memory/vector_store.py @@ -19,7 +19,6 @@ import numpy as np from llama_models.llama3.api.tokenizer import Tokenizer from numpy.typing import NDArray -from pydantic import BaseModel, Field from pypdf import PdfReader from llama_stack.apis.common.content_types import ( @@ -27,6 +26,7 @@ from llama_stack.apis.common.content_types import ( TextContentItem, URL, ) +from llama_stack.apis.tools import RAGDocument from llama_stack.apis.vector_dbs import VectorDB from llama_stack.apis.vector_io import Chunk, QueryChunksResponse from llama_stack.providers.datatypes import Api @@ -34,17 +34,9 @@ from llama_stack.providers.utils.inference.prompt_adapter import ( interleaved_content_as_str, ) - log = logging.getLogger(__name__) -class MemoryBankDocument(BaseModel): - document_id: str - content: InterleavedContent | URL - mime_type: str | None = None - metadata: Dict[str, Any] = Field(default_factory=dict) - - def parse_pdf(data: bytes) -> str: # For PDF and DOC/DOCX files, we can't reliably convert to string pdf_bytes = io.BytesIO(data) @@ -122,7 +114,7 @@ def concat_interleaved_content(content: List[InterleavedContent]) -> Interleaved return ret -async def content_from_doc(doc: MemoryBankDocument) -> str: +async def content_from_doc(doc: RAGDocument) -> str: if isinstance(doc.content, URL): if doc.content.uri.startswith("data:"): return content_from_data(doc.content.uri) @@ -161,7 +153,13 @@ def make_overlapped_chunks( chunk = tokenizer.decode(toks) # chunk is a string chunks.append( - Chunk(content=chunk, token_count=len(toks), document_id=document_id) + Chunk( + content=chunk, + metadata={ + "token_count": len(toks), + "document_id": document_id, + }, + ) ) return chunks diff --git a/llama_stack/scripts/test_rag_via_curl.py b/llama_stack/scripts/test_rag_via_curl.py new file mode 100644 index 000000000..28d6fb601 --- /dev/null +++ b/llama_stack/scripts/test_rag_via_curl.py @@ -0,0 +1,105 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +import json +from typing import List + +import pytest +import requests +from pydantic import TypeAdapter + +from llama_stack.apis.tools import ( + DefaultRAGQueryGeneratorConfig, + RAGDocument, + RAGQueryConfig, + RAGQueryResult, +) +from llama_stack.apis.vector_dbs import VectorDB +from llama_stack.providers.utils.memory.vector_store import interleaved_content_as_str + + +class TestRAGToolEndpoints: + @pytest.fixture + def base_url(self) -> str: + return "http://localhost:8321/v1" # Adjust port if needed + + @pytest.fixture + def sample_documents(self) -> List[RAGDocument]: + return [ + RAGDocument( + document_id="doc1", + content="Python is a high-level programming language.", + metadata={"category": "programming", "difficulty": "beginner"}, + ), + RAGDocument( + document_id="doc2", + content="Machine learning is a subset of artificial intelligence.", + metadata={"category": "AI", "difficulty": "advanced"}, + ), + RAGDocument( + document_id="doc3", + content="Data structures are fundamental to computer science.", + metadata={"category": "computer science", "difficulty": "intermediate"}, + ), + ] + + @pytest.mark.asyncio + async def test_rag_workflow( + self, base_url: str, sample_documents: List[RAGDocument] + ): + vector_db_payload = { + "vector_db_id": "test_vector_db", + "embedding_model": "all-MiniLM-L6-v2", + "embedding_dimension": 384, + } + + response = requests.post(f"{base_url}/vector-dbs", json=vector_db_payload) + assert response.status_code == 200 + vector_db = VectorDB(**response.json()) + + insert_payload = { + "documents": [ + json.loads(doc.model_dump_json()) for doc in sample_documents + ], + "vector_db_id": vector_db.identifier, + "chunk_size_in_tokens": 512, + } + + response = requests.post( + f"{base_url}/tool-runtime/rag-tool/insert-documents", + json=insert_payload, + ) + assert response.status_code == 200 + + query = "What is Python?" + query_config = RAGQueryConfig( + query_generator_config=DefaultRAGQueryGeneratorConfig(), + max_tokens_in_context=4096, + max_chunks=2, + ) + + query_payload = { + "content": query, + "query_config": json.loads(query_config.model_dump_json()), + "vector_db_ids": [vector_db.identifier], + } + + response = requests.post( + f"{base_url}/tool-runtime/rag-tool/query-context", + json=query_payload, + ) + assert response.status_code == 200 + result = response.json() + result = TypeAdapter(RAGQueryResult).validate_python(result) + + content_str = interleaved_content_as_str(result.content) + print(f"content: {content_str}") + assert len(content_str) > 0 + assert "Python" in content_str + + # Clean up: Delete the vector DB + response = requests.delete(f"{base_url}/vector-dbs/{vector_db.identifier}") + assert response.status_code == 200 diff --git a/llama_stack/templates/together/build.yaml b/llama_stack/templates/together/build.yaml index ea7387a24..2160adb8e 100644 --- a/llama_stack/templates/together/build.yaml +++ b/llama_stack/templates/together/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::together - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/together/run.yaml b/llama_stack/templates/together/run.yaml index da25fd144..135b124e4 100644 --- a/llama_stack/templates/together/run.yaml +++ b/llama_stack/templates/together/run.yaml @@ -5,7 +5,7 @@ apis: - datasetio - eval - inference -- memory +- vector_io - safety - scoring - telemetry @@ -20,7 +20,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -145,7 +145,6 @@ models: model_type: embedding shields: - shield_id: meta-llama/Llama-Guard-3-8B -memory_banks: [] datasets: [] scoring_fns: [] eval_tasks: [] From 63f37f9b7c663b8b30c008c9061dd085f3935e81 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 10:15:19 -0800 Subject: [PATCH 072/107] [memory refactor][4/n] Update the client-sdk test for RAG (#834) See https://github.com/meta-llama/llama-stack/issues/827 for the broader design. Update client-sdk tests --- tests/client-sdk/agents/test_agents.py | 23 +- .../client-sdk/tool_runtime/test_rag_tool.py | 180 ++++++++++++ tests/client-sdk/vector_io/test_vector_io.py | 261 +++--------------- 3 files changed, 236 insertions(+), 228 deletions(-) create mode 100644 tests/client-sdk/tool_runtime/test_rag_tool.py diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py index 36fe2843d..fe80100da 100644 --- a/tests/client-sdk/agents/test_agents.py +++ b/tests/client-sdk/agents/test_agents.py @@ -286,19 +286,16 @@ def test_rag_agent(llama_stack_client, agent_config): ) for i, url in enumerate(urls) ] - memory_bank_id = "test-memory-bank" - llama_stack_client.memory_banks.register( - memory_bank_id=memory_bank_id, - params={ - "memory_bank_type": "vector", - "embedding_model": "all-MiniLM-L6-v2", - "chunk_size_in_tokens": 512, - "overlap_size_in_tokens": 64, - }, + vector_db_id = "test-vector-db" + llama_stack_client.vector_dbs.register( + vector_db_id=vector_db_id, + embedding_model="all-MiniLM-L6-v2", + embedding_dimension=384, ) - llama_stack_client.memory.insert( - bank_id=memory_bank_id, + llama_stack_client.tool_runtime.rag_tool.insert_documents( documents=documents, + vector_db_id=vector_db_id, + chunk_size_in_tokens=512, ) agent_config = { **agent_config, @@ -306,7 +303,7 @@ def test_rag_agent(llama_stack_client, agent_config): dict( name="builtin::memory", args={ - "memory_bank_ids": [memory_bank_id], + "vector_db_ids": [vector_db_id], }, ) ], @@ -324,4 +321,4 @@ def test_rag_agent(llama_stack_client, agent_config): ) logs = [str(log) for log in EventLogger().log(response) if log is not None] logs_str = "".join(logs) - assert "Tool:query_memory" in logs_str + assert "Tool:rag_tool.query_context" in logs_str diff --git a/tests/client-sdk/tool_runtime/test_rag_tool.py b/tests/client-sdk/tool_runtime/test_rag_tool.py new file mode 100644 index 000000000..bce067268 --- /dev/null +++ b/tests/client-sdk/tool_runtime/test_rag_tool.py @@ -0,0 +1,180 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +import random + +import pytest + +from llama_stack_client.types.tool_runtime import DocumentParam + + +@pytest.fixture(scope="function") +def empty_vector_db_registry(llama_stack_client): + vector_dbs = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() + ] + for vector_db_id in vector_dbs: + llama_stack_client.vector_dbs.unregister(vector_db_id=vector_db_id) + + +@pytest.fixture(scope="function") +def single_entry_vector_db_registry(llama_stack_client, empty_vector_db_registry): + vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}" + llama_stack_client.vector_dbs.register( + vector_db_id=vector_db_id, + embedding_model="all-MiniLM-L6-v2", + embedding_dimension=384, + provider_id="faiss", + ) + vector_dbs = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() + ] + return vector_dbs + + +@pytest.fixture(scope="session") +def sample_documents(): + return [ + DocumentParam( + document_id="test-doc-1", + content="Python is a high-level programming language.", + metadata={"category": "programming", "difficulty": "beginner"}, + ), + DocumentParam( + document_id="test-doc-2", + content="Machine learning is a subset of artificial intelligence.", + metadata={"category": "AI", "difficulty": "advanced"}, + ), + DocumentParam( + document_id="test-doc-3", + content="Data structures are fundamental to computer science.", + metadata={"category": "computer science", "difficulty": "intermediate"}, + ), + DocumentParam( + document_id="test-doc-4", + content="Neural networks are inspired by biological neural networks.", + metadata={"category": "AI", "difficulty": "advanced"}, + ), + ] + + +def assert_valid_response(response): + assert len(response.chunks) > 0 + assert len(response.scores) > 0 + assert len(response.chunks) == len(response.scores) + for chunk in response.chunks: + assert isinstance(chunk.content, str) + + +def test_vector_db_insert_inline_and_query( + llama_stack_client, single_entry_vector_db_registry, sample_documents +): + vector_db_id = single_entry_vector_db_registry[0] + llama_stack_client.tool_runtime.rag_tool.insert_documents( + documents=sample_documents, + chunk_size_in_tokens=512, + vector_db_id=vector_db_id, + ) + + # Query with a direct match + query1 = "programming language" + response1 = llama_stack_client.vector_io.query( + vector_db_id=vector_db_id, + query=query1, + ) + assert_valid_response(response1) + assert any("Python" in chunk.content for chunk in response1.chunks) + + # Query with semantic similarity + query2 = "AI and brain-inspired computing" + response2 = llama_stack_client.vector_io.query( + vector_db_id=vector_db_id, + query=query2, + ) + assert_valid_response(response2) + assert any("neural networks" in chunk.content.lower() for chunk in response2.chunks) + + # Query with limit on number of results (max_chunks=2) + query3 = "computer" + response3 = llama_stack_client.vector_io.query( + vector_db_id=vector_db_id, + query=query3, + params={"max_chunks": 2}, + ) + assert_valid_response(response3) + assert len(response3.chunks) <= 2 + + # Query with threshold on similarity score + query4 = "computer" + response4 = llama_stack_client.vector_io.query( + vector_db_id=vector_db_id, + query=query4, + params={"score_threshold": 0.01}, + ) + assert_valid_response(response4) + assert all(score >= 0.01 for score in response4.scores) + + +def test_vector_db_insert_from_url_and_query( + llama_stack_client, empty_vector_db_registry +): + providers = [p for p in llama_stack_client.providers.list() if p.api == "vector_io"] + assert len(providers) > 0 + + vector_db_id = "test_vector_db" + + llama_stack_client.vector_dbs.register( + vector_db_id=vector_db_id, + embedding_model="all-MiniLM-L6-v2", + embedding_dimension=384, + provider_id="faiss", + ) + + # list to check memory bank is successfully registered + available_vector_dbs = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() + ] + assert vector_db_id in available_vector_dbs + + # URLs of documents to insert + # TODO: Move to test/memory/resources then update the url to + # https://raw.githubusercontent.com/meta-llama/llama-stack/main/tests/memory/resources/{url} + urls = [ + "memory_optimizations.rst", + "chat.rst", + "llama3.rst", + ] + documents = [ + DocumentParam( + document_id=f"num-{i}", + content=f"https://raw.githubusercontent.com/pytorch/torchtune/main/docs/source/tutorials/{url}", + mime_type="text/plain", + metadata={}, + ) + for i, url in enumerate(urls) + ] + + llama_stack_client.tool_runtime.rag_tool.insert_documents( + documents=documents, + vector_db_id=vector_db_id, + chunk_size_in_tokens=512, + ) + + # Query for the name of method + response1 = llama_stack_client.vector_io.query( + vector_db_id=vector_db_id, + query="What's the name of the fine-tunning method used?", + ) + assert_valid_response(response1) + assert any("lora" in chunk.content.lower() for chunk in response1.chunks) + + # Query for the name of model + response2 = llama_stack_client.vector_io.query( + vector_db_id=vector_db_id, + query="Which Llama model is mentioned?", + ) + assert_valid_response(response2) + assert any("llama2" in chunk.content.lower() for chunk in response2.chunks) diff --git a/tests/client-sdk/vector_io/test_vector_io.py b/tests/client-sdk/vector_io/test_vector_io.py index 1e9b34355..04b639667 100644 --- a/tests/client-sdk/vector_io/test_vector_io.py +++ b/tests/client-sdk/vector_io/test_vector_io.py @@ -8,251 +8,82 @@ import random import pytest -from llama_stack.apis.memory import MemoryBankDocument -from llama_stack_client.types.memory_insert_params import Document - @pytest.fixture(scope="function") -def empty_memory_bank_registry(llama_stack_client): - memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() +def empty_vector_db_registry(llama_stack_client): + vector_dbs = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() ] - for memory_bank_id in memory_banks: - llama_stack_client.memory_banks.unregister(memory_bank_id=memory_bank_id) + for vector_db_id in vector_dbs: + llama_stack_client.vector_dbs.unregister(vector_db_id=vector_db_id) @pytest.fixture(scope="function") -def single_entry_memory_bank_registry(llama_stack_client, empty_memory_bank_registry): - memory_bank_id = f"test_bank_{random.randint(1000, 9999)}" - llama_stack_client.memory_banks.register( - memory_bank_id=memory_bank_id, - params={ - "memory_bank_type": "vector", - "embedding_model": "all-MiniLM-L6-v2", - "chunk_size_in_tokens": 512, - "overlap_size_in_tokens": 64, - }, +def single_entry_vector_db_registry(llama_stack_client, empty_vector_db_registry): + vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}" + llama_stack_client.vector_dbs.register( + vector_db_id=vector_db_id, + embedding_model="all-MiniLM-L6-v2", + embedding_dimension=384, provider_id="faiss", ) - memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + vector_dbs = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() ] - return memory_banks + return vector_dbs -@pytest.fixture(scope="session") -def sample_documents(): - return [ - MemoryBankDocument( - document_id="test-doc-1", - content="Python is a high-level programming language.", - metadata={"category": "programming", "difficulty": "beginner"}, - ), - MemoryBankDocument( - document_id="test-doc-2", - content="Machine learning is a subset of artificial intelligence.", - metadata={"category": "AI", "difficulty": "advanced"}, - ), - MemoryBankDocument( - document_id="test-doc-3", - content="Data structures are fundamental to computer science.", - metadata={"category": "computer science", "difficulty": "intermediate"}, - ), - MemoryBankDocument( - document_id="test-doc-4", - content="Neural networks are inspired by biological neural networks.", - metadata={"category": "AI", "difficulty": "advanced"}, - ), - ] - - -def assert_valid_response(response): - assert len(response.chunks) > 0 - assert len(response.scores) > 0 - assert len(response.chunks) == len(response.scores) - for chunk in response.chunks: - assert isinstance(chunk.content, str) - assert chunk.document_id is not None - - -def test_memory_bank_retrieve(llama_stack_client, empty_memory_bank_registry): +def test_vector_db_retrieve(llama_stack_client, empty_vector_db_registry): # Register a memory bank first - memory_bank_id = f"test_bank_{random.randint(1000, 9999)}" - llama_stack_client.memory_banks.register( - memory_bank_id=memory_bank_id, - params={ - "memory_bank_type": "vector", - "embedding_model": "all-MiniLM-L6-v2", - "chunk_size_in_tokens": 512, - "overlap_size_in_tokens": 64, - }, + vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}" + llama_stack_client.vector_dbs.register( + vector_db_id=vector_db_id, + embedding_model="all-MiniLM-L6-v2", + embedding_dimension=384, provider_id="faiss", ) # Retrieve the memory bank and validate its properties - response = llama_stack_client.memory_banks.retrieve(memory_bank_id=memory_bank_id) + response = llama_stack_client.vector_dbs.retrieve(vector_db_id=vector_db_id) assert response is not None - assert response.identifier == memory_bank_id - assert response.type == "memory_bank" - assert response.memory_bank_type == "vector" + assert response.identifier == vector_db_id assert response.embedding_model == "all-MiniLM-L6-v2" - assert response.chunk_size_in_tokens == 512 - assert response.overlap_size_in_tokens == 64 assert response.provider_id == "faiss" - assert response.provider_resource_id == memory_bank_id + assert response.provider_resource_id == vector_db_id -def test_memory_bank_list(llama_stack_client, empty_memory_bank_registry): - memory_banks_after_register = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() +def test_vector_db_list(llama_stack_client, empty_vector_db_registry): + vector_dbs_after_register = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() ] - assert len(memory_banks_after_register) == 0 + assert len(vector_dbs_after_register) == 0 -def test_memory_bank_register(llama_stack_client, empty_memory_bank_registry): - memory_provider_id = "faiss" - memory_bank_id = f"test_bank_{random.randint(1000, 9999)}" - llama_stack_client.memory_banks.register( - memory_bank_id=memory_bank_id, - params={ - "memory_bank_type": "vector", - "embedding_model": "all-MiniLM-L6-v2", - "chunk_size_in_tokens": 512, - "overlap_size_in_tokens": 64, - }, - provider_id=memory_provider_id, +def test_vector_db_register(llama_stack_client, empty_vector_db_registry): + vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}" + llama_stack_client.vector_dbs.register( + vector_db_id=vector_db_id, + embedding_model="all-MiniLM-L6-v2", + embedding_dimension=384, + provider_id="faiss", ) - memory_banks_after_register = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + vector_dbs_after_register = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() ] - assert memory_banks_after_register == [memory_bank_id] + assert vector_dbs_after_register == [vector_db_id] -def test_memory_bank_unregister(llama_stack_client, single_entry_memory_bank_registry): - memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() +def test_vector_db_unregister(llama_stack_client, single_entry_vector_db_registry): + vector_dbs = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() ] - assert len(memory_banks) == 1 + assert len(vector_dbs) == 1 - memory_bank_id = memory_banks[0] - llama_stack_client.memory_banks.unregister(memory_bank_id=memory_bank_id) + vector_db_id = vector_dbs[0] + llama_stack_client.vector_dbs.unregister(vector_db_id=vector_db_id) - memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() + vector_dbs = [ + vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list() ] - assert len(memory_banks) == 0 - - -def test_memory_bank_insert_inline_and_query( - llama_stack_client, single_entry_memory_bank_registry, sample_documents -): - memory_bank_id = single_entry_memory_bank_registry[0] - llama_stack_client.memory.insert( - bank_id=memory_bank_id, - documents=sample_documents, - ) - - # Query with a direct match - query1 = "programming language" - response1 = llama_stack_client.memory.query( - bank_id=memory_bank_id, - query=query1, - ) - assert_valid_response(response1) - assert any("Python" in chunk.content for chunk in response1.chunks) - - # Query with semantic similarity - query2 = "AI and brain-inspired computing" - response2 = llama_stack_client.memory.query( - bank_id=memory_bank_id, - query=query2, - ) - assert_valid_response(response2) - assert any("neural networks" in chunk.content.lower() for chunk in response2.chunks) - - # Query with limit on number of results (max_chunks=2) - query3 = "computer" - response3 = llama_stack_client.memory.query( - bank_id=memory_bank_id, - query=query3, - params={"max_chunks": 2}, - ) - assert_valid_response(response3) - assert len(response3.chunks) <= 2 - - # Query with threshold on similarity score - query4 = "computer" - response4 = llama_stack_client.memory.query( - bank_id=memory_bank_id, - query=query4, - params={"score_threshold": 0.01}, - ) - assert_valid_response(response4) - assert all(score >= 0.01 for score in response4.scores) - - -def test_memory_bank_insert_from_url_and_query( - llama_stack_client, empty_memory_bank_registry -): - providers = [p for p in llama_stack_client.providers.list() if p.api == "memory"] - assert len(providers) > 0 - - memory_provider_id = providers[0].provider_id - memory_bank_id = "test_bank" - - llama_stack_client.memory_banks.register( - memory_bank_id=memory_bank_id, - params={ - "memory_bank_type": "vector", - "embedding_model": "all-MiniLM-L6-v2", - "chunk_size_in_tokens": 512, - "overlap_size_in_tokens": 64, - }, - provider_id=memory_provider_id, - ) - - # list to check memory bank is successfully registered - available_memory_banks = [ - memory_bank.identifier for memory_bank in llama_stack_client.memory_banks.list() - ] - assert memory_bank_id in available_memory_banks - - # URLs of documents to insert - # TODO: Move to test/memory/resources then update the url to - # https://raw.githubusercontent.com/meta-llama/llama-stack/main/tests/memory/resources/{url} - urls = [ - "memory_optimizations.rst", - "chat.rst", - "llama3.rst", - ] - documents = [ - Document( - document_id=f"num-{i}", - content=f"https://raw.githubusercontent.com/pytorch/torchtune/main/docs/source/tutorials/{url}", - mime_type="text/plain", - metadata={}, - ) - for i, url in enumerate(urls) - ] - - llama_stack_client.memory.insert( - bank_id=memory_bank_id, - documents=documents, - ) - - # Query for the name of method - response1 = llama_stack_client.memory.query( - bank_id=memory_bank_id, - query="What's the name of the fine-tunning method used?", - ) - assert_valid_response(response1) - assert any("lora" in chunk.content.lower() for chunk in response1.chunks) - - # Query for the name of model - response2 = llama_stack_client.memory.query( - bank_id=memory_bank_id, - query="Which Llama model is mentioned?", - ) - assert_valid_response(response1) - assert any("llama2" in chunk.content.lower() for chunk in response2.chunks) + assert len(vector_dbs) == 0 From c9e5578151ae612fce300aecc4c4d9f830107dae Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 10:17:59 -0800 Subject: [PATCH 073/107] [memory refactor][5/n] Migrate all vector_io providers (#835) See https://github.com/meta-llama/llama-stack/issues/827 for the broader design. This PR finishes off all the stragglers and migrates everything to the new naming. --- .../remote_hosted_distro/nvidia.md | 2 +- .../self_hosted_distro/bedrock.md | 2 +- .../self_hosted_distro/cerebras.md | 2 +- .../self_hosted_distro/fireworks.md | 2 +- .../self_hosted_distro/meta-reference-gpu.md | 2 +- .../meta-reference-quantized-gpu.md | 2 +- .../self_hosted_distro/ollama.md | 2 +- .../self_hosted_distro/remote-vllm.md | 2 +- .../distributions/self_hosted_distro/tgi.md | 2 +- .../self_hosted_distro/together.md | 2 +- llama_stack/apis/agents/agents.py | 2 +- llama_stack/apis/agents/event_logger.py | 2 +- llama_stack/apis/resource.py | 2 +- .../distribution/store/tests/test_registry.py | 140 +++++++++--------- .../ui/page/distribution/memory_banks.py | 23 --- .../ui/page/distribution/resources.py | 8 +- .../ui/page/distribution/vector_dbs.py | 23 +++ .../distribution/ui/page/playground/rag.py | 54 ++++--- .../agents/meta_reference/agent_instance.py | 26 ++-- .../agents/meta_reference/persistence.py | 6 +- .../meta_reference/tests/test_chat_agent.py | 116 +++------------ .../inline/vector_io/chroma/__init__.py | 6 +- .../remote/vector_io/chroma/__init__.py | 4 +- .../remote/vector_io/chroma/chroma.py | 93 ++++++------ .../remote/vector_io/pgvector/pgvector.py | 90 ++++++----- .../remote/vector_io/qdrant/qdrant.py | 83 +++++------ .../remote/vector_io/sample/sample.py | 13 +- .../remote/vector_io/weaviate/weaviate.py | 91 ++++++------ llama_stack/providers/tests/eval/fixtures.py | 4 +- llama_stack/providers/tests/tools/fixtures.py | 9 +- .../providers/tests/tools/test_tools.py | 47 +++--- llama_stack/templates/bedrock/bedrock.py | 8 +- llama_stack/templates/bedrock/build.yaml | 2 +- llama_stack/templates/bedrock/run.yaml | 6 +- llama_stack/templates/cerebras/build.yaml | 2 +- llama_stack/templates/cerebras/cerebras.py | 8 +- llama_stack/templates/cerebras/run.yaml | 6 +- .../experimental-post-training/run.yaml | 4 +- llama_stack/templates/fireworks/build.yaml | 2 +- llama_stack/templates/fireworks/fireworks.py | 10 +- .../templates/fireworks/run-with-safety.yaml | 6 +- llama_stack/templates/fireworks/run.yaml | 6 +- llama_stack/templates/hf-endpoint/build.yaml | 2 +- .../templates/hf-endpoint/hf_endpoint.py | 10 +- .../hf-endpoint/run-with-safety.yaml | 6 +- llama_stack/templates/hf-endpoint/run.yaml | 6 +- .../templates/hf-serverless/build.yaml | 2 +- .../templates/hf-serverless/hf_serverless.py | 10 +- .../hf-serverless/run-with-safety.yaml | 6 +- llama_stack/templates/hf-serverless/run.yaml | 6 +- .../templates/meta-reference-gpu/build.yaml | 2 +- .../meta-reference-gpu/meta_reference.py | 10 +- .../meta-reference-gpu/run-with-safety.yaml | 6 +- .../templates/meta-reference-gpu/run.yaml | 6 +- .../meta-reference-quantized-gpu/build.yaml | 2 +- .../meta_reference.py | 8 +- .../meta-reference-quantized-gpu/run.yaml | 6 +- llama_stack/templates/nvidia/build.yaml | 2 +- llama_stack/templates/nvidia/nvidia.py | 2 +- llama_stack/templates/nvidia/run.yaml | 6 +- llama_stack/templates/ollama/build.yaml | 2 +- llama_stack/templates/ollama/ollama.py | 10 +- .../templates/ollama/run-with-safety.yaml | 6 +- llama_stack/templates/ollama/run.yaml | 6 +- llama_stack/templates/remote-vllm/build.yaml | 2 +- .../remote-vllm/run-with-safety.yaml | 6 +- llama_stack/templates/remote-vllm/run.yaml | 6 +- llama_stack/templates/remote-vllm/vllm.py | 10 +- llama_stack/templates/tgi/build.yaml | 2 +- .../templates/tgi/run-with-safety.yaml | 6 +- llama_stack/templates/tgi/run.yaml | 6 +- llama_stack/templates/tgi/tgi.py | 10 +- .../templates/together/run-with-safety.yaml | 6 +- llama_stack/templates/together/run.yaml | 3 +- llama_stack/templates/together/together.py | 10 +- llama_stack/templates/vllm-gpu/build.yaml | 2 +- llama_stack/templates/vllm-gpu/run.yaml | 6 +- llama_stack/templates/vllm-gpu/vllm.py | 8 +- 78 files changed, 504 insertions(+), 623 deletions(-) delete mode 100644 llama_stack/distribution/ui/page/distribution/memory_banks.py create mode 100644 llama_stack/distribution/ui/page/distribution/vector_dbs.py diff --git a/docs/source/distributions/remote_hosted_distro/nvidia.md b/docs/source/distributions/remote_hosted_distro/nvidia.md index 4028ed384..e4c3a155f 100644 --- a/docs/source/distributions/remote_hosted_distro/nvidia.md +++ b/docs/source/distributions/remote_hosted_distro/nvidia.md @@ -8,11 +8,11 @@ The `llamastack/distribution-nvidia` distribution consists of the following prov | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `remote::nvidia` | -| memory | `inline::faiss` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` | +| vector_io | `inline::faiss` | ### Environment Variables diff --git a/docs/source/distributions/self_hosted_distro/bedrock.md b/docs/source/distributions/self_hosted_distro/bedrock.md index dd4e51264..a66325560 100644 --- a/docs/source/distributions/self_hosted_distro/bedrock.md +++ b/docs/source/distributions/self_hosted_distro/bedrock.md @@ -15,11 +15,11 @@ The `llamastack/distribution-bedrock` distribution consists of the following pro | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `remote::bedrock` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `remote::bedrock` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | diff --git a/docs/source/distributions/self_hosted_distro/cerebras.md b/docs/source/distributions/self_hosted_distro/cerebras.md index 22e4125bd..211082b7a 100644 --- a/docs/source/distributions/self_hosted_distro/cerebras.md +++ b/docs/source/distributions/self_hosted_distro/cerebras.md @@ -8,11 +8,11 @@ The `llamastack/distribution-cerebras` distribution consists of the following pr | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `remote::cerebras` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | ### Environment Variables diff --git a/docs/source/distributions/self_hosted_distro/fireworks.md b/docs/source/distributions/self_hosted_distro/fireworks.md index 7ed174984..39043b1c1 100644 --- a/docs/source/distributions/self_hosted_distro/fireworks.md +++ b/docs/source/distributions/self_hosted_distro/fireworks.md @@ -18,11 +18,11 @@ The `llamastack/distribution-fireworks` distribution consists of the following p | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `remote::fireworks` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | ### Environment Variables diff --git a/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md b/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md index 269354e98..8475aab3a 100644 --- a/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md +++ b/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md @@ -18,11 +18,11 @@ The `llamastack/distribution-meta-reference-gpu` distribution consists of the fo | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `inline::meta-reference` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | Note that you need access to nvidia GPUs to run this distribution. This distribution is not compatible with CPU-only machines or machines with AMD GPUs. diff --git a/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md b/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md index 937dbbdbd..6f1adb5a9 100644 --- a/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md +++ b/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md @@ -18,11 +18,11 @@ The `llamastack/distribution-meta-reference-quantized-gpu` distribution consists | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `inline::meta-reference-quantized` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | The only difference vs. the `meta-reference-gpu` distribution is that it has support for more efficient inference -- with fp8, int4 quantization, etc. diff --git a/docs/source/distributions/self_hosted_distro/ollama.md b/docs/source/distributions/self_hosted_distro/ollama.md index e8e5dd397..f5ba31feb 100644 --- a/docs/source/distributions/self_hosted_distro/ollama.md +++ b/docs/source/distributions/self_hosted_distro/ollama.md @@ -18,11 +18,11 @@ The `llamastack/distribution-ollama` distribution consists of the following prov | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `remote::ollama` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | You should use this distribution if you have a regular desktop machine without very powerful GPUs. Of course, if you have powerful GPUs, you can still continue using this distribution since Ollama supports GPU acceleration.### Environment Variables diff --git a/docs/source/distributions/self_hosted_distro/remote-vllm.md b/docs/source/distributions/self_hosted_distro/remote-vllm.md index 2bb5329b9..c2b3544d3 100644 --- a/docs/source/distributions/self_hosted_distro/remote-vllm.md +++ b/docs/source/distributions/self_hosted_distro/remote-vllm.md @@ -17,11 +17,11 @@ The `llamastack/distribution-remote-vllm` distribution consists of the following | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `remote::vllm` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | You can use this distribution if you have GPUs and want to run an independent vLLM server container for running inference. diff --git a/docs/source/distributions/self_hosted_distro/tgi.md b/docs/source/distributions/self_hosted_distro/tgi.md index 0fd6a693c..c21a6a586 100644 --- a/docs/source/distributions/self_hosted_distro/tgi.md +++ b/docs/source/distributions/self_hosted_distro/tgi.md @@ -19,11 +19,11 @@ The `llamastack/distribution-tgi` distribution consists of the following provide | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `remote::tgi` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | You can use this distribution if you have GPUs and want to run an independent TGI server container for running inference. diff --git a/docs/source/distributions/self_hosted_distro/together.md b/docs/source/distributions/self_hosted_distro/together.md index e990e273f..65a711522 100644 --- a/docs/source/distributions/self_hosted_distro/together.md +++ b/docs/source/distributions/self_hosted_distro/together.md @@ -18,11 +18,11 @@ The `llamastack/distribution-together` distribution consists of the following pr | datasetio | `remote::huggingface`, `inline::localfs` | | eval | `inline::meta-reference` | | inference | `remote::together` | -| memory | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | | safety | `inline::llama-guard` | | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` | | telemetry | `inline::meta-reference` | | tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` | +| vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` | ### Environment Variables diff --git a/llama_stack/apis/agents/agents.py b/llama_stack/apis/agents/agents.py index 20cb8f828..c19f28054 100644 --- a/llama_stack/apis/agents/agents.py +++ b/llama_stack/apis/agents/agents.py @@ -88,7 +88,7 @@ class MemoryRetrievalStep(StepCommon): step_type: Literal[StepType.memory_retrieval.value] = ( StepType.memory_retrieval.value ) - memory_bank_ids: List[str] + vector_db_ids: str inserted_context: InterleavedContent diff --git a/llama_stack/apis/agents/event_logger.py b/llama_stack/apis/agents/event_logger.py index 9e2f14805..ddb2a7cf4 100644 --- a/llama_stack/apis/agents/event_logger.py +++ b/llama_stack/apis/agents/event_logger.py @@ -208,7 +208,7 @@ class EventLogger: ): details = event.payload.step_details inserted_context = interleaved_content_as_str(details.inserted_context) - content = f"fetched {len(inserted_context)} bytes from {details.memory_bank_ids}" + content = f"fetched {len(inserted_context)} bytes from {details.vector_db_ids}" yield ( event, diff --git a/llama_stack/apis/resource.py b/llama_stack/apis/resource.py index dfe3ddb24..d0ce72644 100644 --- a/llama_stack/apis/resource.py +++ b/llama_stack/apis/resource.py @@ -37,5 +37,5 @@ class Resource(BaseModel): provider_id: str = Field(description="ID of the provider that owns this resource") type: ResourceType = Field( - description="Type of resource (e.g. 'model', 'shield', 'memory_bank', etc.)" + description="Type of resource (e.g. 'model', 'shield', 'vector_db', etc.)" ) diff --git a/llama_stack/distribution/store/tests/test_registry.py b/llama_stack/distribution/store/tests/test_registry.py index 9c5b72f93..78d59a088 100644 --- a/llama_stack/distribution/store/tests/test_registry.py +++ b/llama_stack/distribution/store/tests/test_registry.py @@ -9,7 +9,7 @@ import os import pytest import pytest_asyncio from llama_stack.apis.inference import Model -from llama_stack.apis.memory_banks import VectorMemoryBank +from llama_stack.apis.vector_dbs import VectorDB from llama_stack.distribution.store.registry import ( CachedDiskDistributionRegistry, @@ -42,13 +42,12 @@ async def cached_registry(config): @pytest.fixture -def sample_bank(): - return VectorMemoryBank( - identifier="test_bank", +def sample_vector_db(): + return VectorDB( + identifier="test_vector_db", embedding_model="all-MiniLM-L6-v2", - chunk_size_in_tokens=512, - overlap_size_in_tokens=64, - provider_resource_id="test_bank", + embedding_dimension=384, + provider_resource_id="test_vector_db", provider_id="test-provider", ) @@ -70,19 +69,17 @@ async def test_registry_initialization(registry): @pytest.mark.asyncio -async def test_basic_registration(registry, sample_bank, sample_model): - print(f"Registering {sample_bank}") - await registry.register(sample_bank) +async def test_basic_registration(registry, sample_vector_db, sample_model): + print(f"Registering {sample_vector_db}") + await registry.register(sample_vector_db) print(f"Registering {sample_model}") await registry.register(sample_model) - print("Getting bank") - result_bank = await registry.get("memory_bank", "test_bank") - assert result_bank is not None - assert result_bank.identifier == sample_bank.identifier - assert result_bank.embedding_model == sample_bank.embedding_model - assert result_bank.chunk_size_in_tokens == sample_bank.chunk_size_in_tokens - assert result_bank.overlap_size_in_tokens == sample_bank.overlap_size_in_tokens - assert result_bank.provider_id == sample_bank.provider_id + print("Getting vector_db") + result_vector_db = await registry.get("vector_db", "test_vector_db") + assert result_vector_db is not None + assert result_vector_db.identifier == sample_vector_db.identifier + assert result_vector_db.embedding_model == sample_vector_db.embedding_model + assert result_vector_db.provider_id == sample_vector_db.provider_id result_model = await registry.get("model", "test_model") assert result_model is not None @@ -91,24 +88,23 @@ async def test_basic_registration(registry, sample_bank, sample_model): @pytest.mark.asyncio -async def test_cached_registry_initialization(config, sample_bank, sample_model): +async def test_cached_registry_initialization(config, sample_vector_db, sample_model): # First populate the disk registry disk_registry = DiskDistributionRegistry(await kvstore_impl(config)) await disk_registry.initialize() - await disk_registry.register(sample_bank) + await disk_registry.register(sample_vector_db) await disk_registry.register(sample_model) # Test cached version loads from disk cached_registry = CachedDiskDistributionRegistry(await kvstore_impl(config)) await cached_registry.initialize() - result_bank = await cached_registry.get("memory_bank", "test_bank") - assert result_bank is not None - assert result_bank.identifier == sample_bank.identifier - assert result_bank.embedding_model == sample_bank.embedding_model - assert result_bank.chunk_size_in_tokens == sample_bank.chunk_size_in_tokens - assert result_bank.overlap_size_in_tokens == sample_bank.overlap_size_in_tokens - assert result_bank.provider_id == sample_bank.provider_id + result_vector_db = await cached_registry.get("vector_db", "test_vector_db") + assert result_vector_db is not None + assert result_vector_db.identifier == sample_vector_db.identifier + assert result_vector_db.embedding_model == sample_vector_db.embedding_model + assert result_vector_db.embedding_dimension == sample_vector_db.embedding_dimension + assert result_vector_db.provider_id == sample_vector_db.provider_id @pytest.mark.asyncio @@ -116,29 +112,28 @@ async def test_cached_registry_updates(config): cached_registry = CachedDiskDistributionRegistry(await kvstore_impl(config)) await cached_registry.initialize() - new_bank = VectorMemoryBank( - identifier="test_bank_2", + new_vector_db = VectorDB( + identifier="test_vector_db_2", embedding_model="all-MiniLM-L6-v2", - chunk_size_in_tokens=256, - overlap_size_in_tokens=32, - provider_resource_id="test_bank_2", + embedding_dimension=384, + provider_resource_id="test_vector_db_2", provider_id="baz", ) - await cached_registry.register(new_bank) + await cached_registry.register(new_vector_db) # Verify in cache - result_bank = await cached_registry.get("memory_bank", "test_bank_2") - assert result_bank is not None - assert result_bank.identifier == new_bank.identifier - assert result_bank.provider_id == new_bank.provider_id + result_vector_db = await cached_registry.get("vector_db", "test_vector_db_2") + assert result_vector_db is not None + assert result_vector_db.identifier == new_vector_db.identifier + assert result_vector_db.provider_id == new_vector_db.provider_id # Verify persisted to disk new_registry = DiskDistributionRegistry(await kvstore_impl(config)) await new_registry.initialize() - result_bank = await new_registry.get("memory_bank", "test_bank_2") - assert result_bank is not None - assert result_bank.identifier == new_bank.identifier - assert result_bank.provider_id == new_bank.provider_id + result_vector_db = await new_registry.get("vector_db", "test_vector_db_2") + assert result_vector_db is not None + assert result_vector_db.identifier == new_vector_db.identifier + assert result_vector_db.provider_id == new_vector_db.provider_id @pytest.mark.asyncio @@ -146,30 +141,28 @@ async def test_duplicate_provider_registration(config): cached_registry = CachedDiskDistributionRegistry(await kvstore_impl(config)) await cached_registry.initialize() - original_bank = VectorMemoryBank( - identifier="test_bank_2", + original_vector_db = VectorDB( + identifier="test_vector_db_2", embedding_model="all-MiniLM-L6-v2", - chunk_size_in_tokens=256, - overlap_size_in_tokens=32, - provider_resource_id="test_bank_2", + embedding_dimension=384, + provider_resource_id="test_vector_db_2", provider_id="baz", ) - await cached_registry.register(original_bank) + await cached_registry.register(original_vector_db) - duplicate_bank = VectorMemoryBank( - identifier="test_bank_2", + duplicate_vector_db = VectorDB( + identifier="test_vector_db_2", embedding_model="different-model", - chunk_size_in_tokens=128, - overlap_size_in_tokens=16, - provider_resource_id="test_bank_2", + embedding_dimension=384, + provider_resource_id="test_vector_db_2", provider_id="baz", # Same provider_id ) - await cached_registry.register(duplicate_bank) + await cached_registry.register(duplicate_vector_db) - result = await cached_registry.get("memory_bank", "test_bank_2") + result = await cached_registry.get("vector_db", "test_vector_db_2") assert result is not None assert ( - result.embedding_model == original_bank.embedding_model + result.embedding_model == original_vector_db.embedding_model ) # Original values preserved @@ -179,36 +172,35 @@ async def test_get_all_objects(config): await cached_registry.initialize() # Create multiple test banks - test_banks = [ - VectorMemoryBank( - identifier=f"test_bank_{i}", + test_vector_dbs = [ + VectorDB( + identifier=f"test_vector_db_{i}", embedding_model="all-MiniLM-L6-v2", - chunk_size_in_tokens=256, - overlap_size_in_tokens=32, - provider_resource_id=f"test_bank_{i}", + embedding_dimension=384, + provider_resource_id=f"test_vector_db_{i}", provider_id=f"provider_{i}", ) for i in range(3) ] - # Register all banks - for bank in test_banks: - await cached_registry.register(bank) + # Register all vector_dbs + for vector_db in test_vector_dbs: + await cached_registry.register(vector_db) # Test get_all retrieval all_results = await cached_registry.get_all() assert len(all_results) == 3 - # Verify each bank was stored correctly - for original_bank in test_banks: - matching_banks = [ - b for b in all_results if b.identifier == original_bank.identifier + # Verify each vector_db was stored correctly + for original_vector_db in test_vector_dbs: + matching_vector_dbs = [ + v for v in all_results if v.identifier == original_vector_db.identifier ] - assert len(matching_banks) == 1 - stored_bank = matching_banks[0] - assert stored_bank.embedding_model == original_bank.embedding_model - assert stored_bank.provider_id == original_bank.provider_id - assert stored_bank.chunk_size_in_tokens == original_bank.chunk_size_in_tokens + assert len(matching_vector_dbs) == 1 + stored_vector_db = matching_vector_dbs[0] + assert stored_vector_db.embedding_model == original_vector_db.embedding_model + assert stored_vector_db.provider_id == original_vector_db.provider_id assert ( - stored_bank.overlap_size_in_tokens == original_bank.overlap_size_in_tokens + stored_vector_db.embedding_dimension + == original_vector_db.embedding_dimension ) diff --git a/llama_stack/distribution/ui/page/distribution/memory_banks.py b/llama_stack/distribution/ui/page/distribution/memory_banks.py deleted file mode 100644 index f28010bf2..000000000 --- a/llama_stack/distribution/ui/page/distribution/memory_banks.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) Meta Platforms, Inc. and affiliates. -# All rights reserved. -# -# This source code is licensed under the terms described in the LICENSE file in -# the root directory of this source tree. - -import streamlit as st -from modules.api import llama_stack_api - - -def memory_banks(): - st.header("Memory Banks") - memory_banks_info = { - m.identifier: m.to_dict() for m in llama_stack_api.client.memory_banks.list() - } - - if len(memory_banks_info) > 0: - selected_memory_bank = st.selectbox( - "Select a memory bank", list(memory_banks_info.keys()) - ) - st.json(memory_banks_info[selected_memory_bank]) - else: - st.info("No memory banks found") diff --git a/llama_stack/distribution/ui/page/distribution/resources.py b/llama_stack/distribution/ui/page/distribution/resources.py index 6b3ea0e3a..38d494570 100644 --- a/llama_stack/distribution/ui/page/distribution/resources.py +++ b/llama_stack/distribution/ui/page/distribution/resources.py @@ -6,10 +6,10 @@ from page.distribution.datasets import datasets from page.distribution.eval_tasks import eval_tasks -from page.distribution.memory_banks import memory_banks from page.distribution.models import models from page.distribution.scoring_functions import scoring_functions from page.distribution.shields import shields +from page.distribution.vector_dbs import vector_dbs from streamlit_option_menu import option_menu @@ -17,7 +17,7 @@ from streamlit_option_menu import option_menu def resources_page(): options = [ "Models", - "Memory Banks", + "Vector Databases", "Shields", "Scoring Functions", "Datasets", @@ -37,8 +37,8 @@ def resources_page(): ) if selected_resource == "Eval Tasks": eval_tasks() - elif selected_resource == "Memory Banks": - memory_banks() + elif selected_resource == "Vector Databases": + vector_dbs() elif selected_resource == "Datasets": datasets() elif selected_resource == "Models": diff --git a/llama_stack/distribution/ui/page/distribution/vector_dbs.py b/llama_stack/distribution/ui/page/distribution/vector_dbs.py new file mode 100644 index 000000000..9afa6de1f --- /dev/null +++ b/llama_stack/distribution/ui/page/distribution/vector_dbs.py @@ -0,0 +1,23 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the terms described in the LICENSE file in +# the root directory of this source tree. + +import streamlit as st +from modules.api import llama_stack_api + + +def vector_dbs(): + st.header("Vector Databases") + vector_dbs_info = { + v.identifier: v.to_dict() for v in llama_stack_api.client.vector_dbs.list() + } + + if len(vector_dbs_info) > 0: + selected_vector_db = st.selectbox( + "Select a vector database", list(vector_dbs_info.keys()) + ) + st.json(vector_dbs_info[selected_vector_db]) + else: + st.info("No vector databases found") diff --git a/llama_stack/distribution/ui/page/playground/rag.py b/llama_stack/distribution/ui/page/playground/rag.py index 11b05718d..465e11560 100644 --- a/llama_stack/distribution/ui/page/playground/rag.py +++ b/llama_stack/distribution/ui/page/playground/rag.py @@ -29,12 +29,12 @@ def rag_chat_page(): if uploaded_files: st.success(f"Successfully uploaded {len(uploaded_files)} files") # Add memory bank name input field - memory_bank_name = st.text_input( - "Memory Bank Name", - value="rag_bank", - help="Enter a unique identifier for this memory bank", + vector_db_name = st.text_input( + "Vector Database Name", + value="rag_vector_db", + help="Enter a unique identifier for this vector database", ) - if st.button("Create Memory Bank"): + if st.button("Create Vector Database"): documents = [ Document( document_id=uploaded_file.name, @@ -44,37 +44,33 @@ def rag_chat_page(): ] providers = llama_stack_api.client.providers.list() - memory_provider = None + vector_io_provider = None for x in providers: - if x.api == "memory": - memory_provider = x.provider_id + if x.api == "vector_io": + vector_io_provider = x.provider_id - llama_stack_api.client.memory_banks.register( - memory_bank_id=memory_bank_name, # Use the user-provided name - params={ - "memory_bank_type": "vector", - "embedding_model": "all-MiniLM-L6-v2", - "chunk_size_in_tokens": 512, - "overlap_size_in_tokens": 64, - }, - provider_id=memory_provider, + llama_stack_api.client.vector_dbs.register( + vector_db_id=vector_db_name, # Use the user-provided name + embedding_dimension=384, + embedding_model="all-MiniLM-L6-v2", + provider_id=vector_io_provider, ) - # insert documents using the custom bank name - llama_stack_api.client.memory.insert( - bank_id=memory_bank_name, # Use the user-provided name + # insert documents using the custom vector db name + llama_stack_api.client.tool_runtime.rag_tool.insert( + vector_db_id=vector_db_name, # Use the user-provided name documents=documents, ) - st.success("Memory bank created successfully!") + st.success("Vector database created successfully!") st.subheader("Configure Agent") # select memory banks - memory_banks = llama_stack_api.client.memory_banks.list() - memory_banks = [bank.identifier for bank in memory_banks] - selected_memory_banks = st.multiselect( - "Select Memory Banks", - memory_banks, + vector_dbs = llama_stack_api.client.vector_dbs.list() + vector_dbs = [vector_db.identifier for vector_db in vector_dbs] + selected_vector_dbs = st.multiselect( + "Select Vector Databases", + vector_dbs, ) available_models = llama_stack_api.client.models.list() @@ -141,14 +137,14 @@ def rag_chat_page(): dict( name="builtin::memory", args={ - "memory_bank_ids": [bank_id for bank_id in selected_memory_banks], + "vector_db_ids": [ + vector_db_id for vector_db_id in selected_vector_dbs + ], }, ) ], tool_choice="auto", tool_prompt_format="json", - input_shields=[], - output_shields=[], enable_session_persistence=False, ) diff --git a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py index 5b5175cee..2d0ad137b 100644 --- a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py +++ b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py @@ -413,8 +413,8 @@ class ChatAgent(ShieldRunnerMixin): session_info = await self.storage.get_session_info(session_id) # if the session has a memory bank id, let the memory tool use it - if session_info.memory_bank_id: - vector_db_ids.append(session_info.memory_bank_id) + if session_info.vector_db_id: + vector_db_ids.append(session_info.vector_db_id) yield AgentTurnResponseStreamChunk( event=AgentTurnResponseEvent( @@ -829,7 +829,7 @@ class ChatAgent(ShieldRunnerMixin): msg = await attachment_message(self.tempdir, url_items) input_messages.append(msg) # Since memory is present, add all the data to the memory bank - await self.add_to_session_memory_bank(session_id, documents) + await self.add_to_session_vector_db(session_id, documents) elif code_interpreter_tool: # if only code_interpreter is available, we download the URLs to a tempdir # and attach the path to them as a message to inference with the @@ -838,7 +838,7 @@ class ChatAgent(ShieldRunnerMixin): input_messages.append(msg) elif memory_tool: # if only memory is available, we load the data from the URLs and content items to the memory bank - await self.add_to_session_memory_bank(session_id, documents) + await self.add_to_session_vector_db(session_id, documents) else: # if no memory or code_interpreter tool is available, # we try to load the data from the URLs and content items as a message to inference @@ -848,31 +848,31 @@ class ChatAgent(ShieldRunnerMixin): + await load_data_from_urls(url_items) ) - async def _ensure_memory_bank(self, session_id: str) -> str: + async def _ensure_vector_db(self, session_id: str) -> str: session_info = await self.storage.get_session_info(session_id) if session_info is None: raise ValueError(f"Session {session_id} not found") - if session_info.memory_bank_id is None: - bank_id = f"memory_bank_{session_id}" + if session_info.vector_db_id is None: + vector_db_id = f"vector_db_{session_id}" # TODO: the semantic for registration is definitely not "creation" # so we need to fix it if we expect the agent to create a new vector db # for each session await self.vector_io_api.register_vector_db( - vector_db_id=bank_id, + vector_db_id=vector_db_id, embedding_model="all-MiniLM-L6-v2", ) - await self.storage.add_memory_bank_to_session(session_id, bank_id) + await self.storage.add_vector_db_to_session(session_id, vector_db_id) else: - bank_id = session_info.memory_bank_id + vector_db_id = session_info.vector_db_id - return bank_id + return vector_db_id - async def add_to_session_memory_bank( + async def add_to_session_vector_db( self, session_id: str, data: List[Document] ) -> None: - vector_db_id = await self._ensure_memory_bank(session_id) + vector_db_id = await self._ensure_vector_db(session_id) documents = [ RAGDocument( document_id=str(uuid.uuid4()), diff --git a/llama_stack/providers/inline/agents/meta_reference/persistence.py b/llama_stack/providers/inline/agents/meta_reference/persistence.py index 58b69858b..4b8ad6d4a 100644 --- a/llama_stack/providers/inline/agents/meta_reference/persistence.py +++ b/llama_stack/providers/inline/agents/meta_reference/persistence.py @@ -21,7 +21,7 @@ log = logging.getLogger(__name__) class AgentSessionInfo(BaseModel): session_id: str session_name: str - memory_bank_id: Optional[str] = None + vector_db_id: Optional[str] = None started_at: datetime @@ -52,12 +52,12 @@ class AgentPersistence: return AgentSessionInfo(**json.loads(value)) - async def add_memory_bank_to_session(self, session_id: str, bank_id: str): + async def add_vector_db_to_session(self, session_id: str, vector_db_id: str): session_info = await self.get_session_info(session_id) if session_info is None: raise ValueError(f"Session {session_id} not found") - session_info.memory_bank_id = bank_id + session_info.vector_db_id = vector_db_id await self.kvstore.set( key=f"session:{self.agent_id}:{session_id}", value=session_info.model_dump_json(), diff --git a/llama_stack/providers/inline/agents/meta_reference/tests/test_chat_agent.py b/llama_stack/providers/inline/agents/meta_reference/tests/test_chat_agent.py index a7e6efc8c..205868279 100644 --- a/llama_stack/providers/inline/agents/meta_reference/tests/test_chat_agent.py +++ b/llama_stack/providers/inline/agents/meta_reference/tests/test_chat_agent.py @@ -29,10 +29,9 @@ from llama_stack.apis.inference import ( SamplingParams, ToolChoice, ToolDefinition, + ToolPromptFormat, UserMessage, ) -from llama_stack.apis.memory import MemoryBank -from llama_stack.apis.memory_banks import BankParams, VectorMemoryBank from llama_stack.apis.safety import RunShieldResponse from llama_stack.apis.tools import ( Tool, @@ -40,8 +39,9 @@ from llama_stack.apis.tools import ( ToolGroup, ToolHost, ToolInvocationResult, - ToolPromptFormat, ) +from llama_stack.apis.vector_io import QueryChunksResponse + from llama_stack.providers.inline.agents.meta_reference.agent_instance import ( MEMORY_QUERY_TOOL, ) @@ -110,68 +110,22 @@ class MockSafetyAPI: return RunShieldResponse(violation=None) -class MockMemoryAPI: +class MockVectorIOAPI: def __init__(self): - self.memory_banks = {} - self.documents = {} + self.chunks = {} - async def create_memory_bank(self, name, config, url=None): - bank_id = f"bank_{len(self.memory_banks)}" - bank = MemoryBank(bank_id, name, config, url) - self.memory_banks[bank_id] = bank - self.documents[bank_id] = {} - return bank + async def insert_chunks(self, vector_db_id, chunks, ttl_seconds=None): + for chunk in chunks: + metadata = chunk.metadata + self.chunks[vector_db_id][metadata["document_id"]] = chunk - async def list_memory_banks(self): - return list(self.memory_banks.values()) + async def query_chunks(self, vector_db_id, query, params=None): + if vector_db_id not in self.chunks: + raise ValueError(f"Bank {vector_db_id} not found") - async def get_memory_bank(self, bank_id): - return self.memory_banks.get(bank_id) - - async def drop_memory_bank(self, bank_id): - if bank_id in self.memory_banks: - del self.memory_banks[bank_id] - del self.documents[bank_id] - return bank_id - - async def insert_documents(self, bank_id, documents, ttl_seconds=None): - if bank_id not in self.documents: - raise ValueError(f"Bank {bank_id} not found") - for doc in documents: - self.documents[bank_id][doc.document_id] = doc - - async def update_documents(self, bank_id, documents): - if bank_id not in self.documents: - raise ValueError(f"Bank {bank_id} not found") - for doc in documents: - if doc.document_id in self.documents[bank_id]: - self.documents[bank_id][doc.document_id] = doc - - async def query_documents(self, bank_id, query, params=None): - if bank_id not in self.documents: - raise ValueError(f"Bank {bank_id} not found") - # Simple mock implementation: return all documents - chunks = [ - {"content": doc.content, "token_count": 10, "document_id": doc.document_id} - for doc in self.documents[bank_id].values() - ] + chunks = list(self.chunks[vector_db_id].values()) scores = [1.0] * len(chunks) - return {"chunks": chunks, "scores": scores} - - async def get_documents(self, bank_id, document_ids): - if bank_id not in self.documents: - raise ValueError(f"Bank {bank_id} not found") - return [ - self.documents[bank_id][doc_id] - for doc_id in document_ids - if doc_id in self.documents[bank_id] - ] - - async def delete_documents(self, bank_id, document_ids): - if bank_id not in self.documents: - raise ValueError(f"Bank {bank_id} not found") - for doc_id in document_ids: - self.documents[bank_id].pop(doc_id, None) + return QueryChunksResponse(chunks=chunks, scores=scores) class MockToolGroupsAPI: @@ -241,31 +195,6 @@ class MockToolRuntimeAPI: return ToolInvocationResult(content={"result": "Mock tool result"}) -class MockMemoryBanksAPI: - async def list_memory_banks(self) -> List[MemoryBank]: - return [] - - async def get_memory_bank(self, memory_bank_id: str) -> Optional[MemoryBank]: - return None - - async def register_memory_bank( - self, - memory_bank_id: str, - params: BankParams, - provider_id: Optional[str] = None, - provider_memory_bank_id: Optional[str] = None, - ) -> MemoryBank: - return VectorMemoryBank( - identifier=memory_bank_id, - provider_resource_id=provider_memory_bank_id or memory_bank_id, - embedding_model="mock_model", - chunk_size_in_tokens=512, - ) - - async def unregister_memory_bank(self, memory_bank_id: str) -> None: - pass - - @pytest.fixture def mock_inference_api(): return MockInferenceAPI() @@ -277,8 +206,8 @@ def mock_safety_api(): @pytest.fixture -def mock_memory_api(): - return MockMemoryAPI() +def mock_vector_io_api(): + return MockVectorIOAPI() @pytest.fixture @@ -291,17 +220,11 @@ def mock_tool_runtime_api(): return MockToolRuntimeAPI() -@pytest.fixture -def mock_memory_banks_api(): - return MockMemoryBanksAPI() - - @pytest.fixture async def get_agents_impl( mock_inference_api, mock_safety_api, - mock_memory_api, - mock_memory_banks_api, + mock_vector_io_api, mock_tool_runtime_api, mock_tool_groups_api, ): @@ -314,8 +237,7 @@ async def get_agents_impl( ), inference_api=mock_inference_api, safety_api=mock_safety_api, - memory_api=mock_memory_api, - memory_banks_api=mock_memory_banks_api, + vector_io_api=mock_vector_io_api, tool_runtime_api=mock_tool_runtime_api, tool_groups_api=mock_tool_groups_api, ) @@ -484,7 +406,7 @@ async def test_chat_agent_tools( toolgroups_for_turn=[ AgentToolGroupWithArgs( name=MEMORY_TOOLGROUP, - args={"memory_banks": ["test_memory_bank"]}, + args={"vector_dbs": ["test_vector_db"]}, ) ] ) diff --git a/llama_stack/providers/inline/vector_io/chroma/__init__.py b/llama_stack/providers/inline/vector_io/chroma/__init__.py index 80620c780..68e28da63 100644 --- a/llama_stack/providers/inline/vector_io/chroma/__init__.py +++ b/llama_stack/providers/inline/vector_io/chroma/__init__.py @@ -14,8 +14,10 @@ from .config import ChromaInlineImplConfig async def get_provider_impl( config: ChromaInlineImplConfig, deps: Dict[Api, ProviderSpec] ): - from llama_stack.providers.remote.memory.chroma.chroma import ChromaMemoryAdapter + from llama_stack.providers.remote.vector_io.chroma.chroma import ( + ChromaVectorIOAdapter, + ) - impl = ChromaMemoryAdapter(config, deps[Api.inference]) + impl = ChromaVectorIOAdapter(config, deps[Api.inference]) await impl.initialize() return impl diff --git a/llama_stack/providers/remote/vector_io/chroma/__init__.py b/llama_stack/providers/remote/vector_io/chroma/__init__.py index 581d60e75..d66a93ac7 100644 --- a/llama_stack/providers/remote/vector_io/chroma/__init__.py +++ b/llama_stack/providers/remote/vector_io/chroma/__init__.py @@ -14,8 +14,8 @@ from .config import ChromaRemoteImplConfig async def get_adapter_impl( config: ChromaRemoteImplConfig, deps: Dict[Api, ProviderSpec] ): - from .chroma import ChromaMemoryAdapter + from .chroma import ChromaVectorIOAdapter - impl = ChromaMemoryAdapter(config, deps[Api.inference]) + impl = ChromaVectorIOAdapter(config, deps[Api.inference]) await impl.initialize() return impl diff --git a/llama_stack/providers/remote/vector_io/chroma/chroma.py b/llama_stack/providers/remote/vector_io/chroma/chroma.py index c04d775ca..724dc3f51 100644 --- a/llama_stack/providers/remote/vector_io/chroma/chroma.py +++ b/llama_stack/providers/remote/vector_io/chroma/chroma.py @@ -6,25 +6,20 @@ import asyncio import json import logging -from typing import List, Optional, Union +from typing import Any, Dict, List, Optional, Union from urllib.parse import urlparse import chromadb from numpy.typing import NDArray from llama_stack.apis.inference import InterleavedContent -from llama_stack.apis.memory import ( - Chunk, - Memory, - MemoryBankDocument, - QueryDocumentsResponse, -) -from llama_stack.apis.memory_banks import MemoryBank, MemoryBankType -from llama_stack.providers.datatypes import Api, MemoryBanksProtocolPrivate -from llama_stack.providers.inline.memory.chroma import ChromaInlineImplConfig +from llama_stack.apis.vector_dbs import VectorDB +from llama_stack.apis.vector_io import Chunk, QueryChunksResponse, VectorIO +from llama_stack.providers.datatypes import Api, VectorDBsProtocolPrivate +from llama_stack.providers.inline.vector_io.chroma import ChromaInlineImplConfig from llama_stack.providers.utils.memory.vector_store import ( - BankWithIndex, EmbeddingIndex, + VectorDBWithIndex, ) from .config import ChromaRemoteImplConfig @@ -61,7 +56,7 @@ class ChromaIndex(EmbeddingIndex): async def query( self, embedding: NDArray, k: int, score_threshold: float - ) -> QueryDocumentsResponse: + ) -> QueryChunksResponse: results = await maybe_await( self.collection.query( query_embeddings=[embedding.tolist()], @@ -85,19 +80,19 @@ class ChromaIndex(EmbeddingIndex): chunks.append(chunk) scores.append(1.0 / float(dist)) - return QueryDocumentsResponse(chunks=chunks, scores=scores) + return QueryChunksResponse(chunks=chunks, scores=scores) async def delete(self): await maybe_await(self.client.delete_collection(self.collection.name)) -class ChromaMemoryAdapter(Memory, MemoryBanksProtocolPrivate): +class ChromaVectorIOAdapter(VectorIO, VectorDBsProtocolPrivate): def __init__( self, config: Union[ChromaRemoteImplConfig, ChromaInlineImplConfig], inference_api: Api.inference, ) -> None: - log.info(f"Initializing ChromaMemoryAdapter with url: {config}") + log.info(f"Initializing ChromaVectorIOAdapter with url: {config}") self.config = config self.inference_api = inference_api @@ -123,60 +118,58 @@ class ChromaMemoryAdapter(Memory, MemoryBanksProtocolPrivate): async def shutdown(self) -> None: pass - async def register_memory_bank( + async def register_vector_db( self, - memory_bank: MemoryBank, + vector_db: VectorDB, ) -> None: - assert ( - memory_bank.memory_bank_type == MemoryBankType.vector.value - ), f"Only vector banks are supported {memory_bank.memory_bank_type}" - collection = await maybe_await( self.client.get_or_create_collection( - name=memory_bank.identifier, - metadata={"bank": memory_bank.model_dump_json()}, + name=vector_db.identifier, + metadata={"vector_db": vector_db.model_dump_json()}, ) ) - self.cache[memory_bank.identifier] = BankWithIndex( - memory_bank, ChromaIndex(self.client, collection), self.inference_api + self.cache[vector_db.identifier] = VectorDBWithIndex( + vector_db, ChromaIndex(self.client, collection), self.inference_api ) - async def unregister_memory_bank(self, memory_bank_id: str) -> None: - await self.cache[memory_bank_id].index.delete() - del self.cache[memory_bank_id] + async def unregister_vector_db(self, vector_db_id: str) -> None: + await self.cache[vector_db_id].index.delete() + del self.cache[vector_db_id] - async def insert_documents( + async def insert_chunks( self, - bank_id: str, - documents: List[MemoryBankDocument], - ttl_seconds: Optional[int] = None, + vector_db_id: str, + chunks: List[Chunk], + embeddings: NDArray, ) -> None: - index = await self._get_and_cache_bank_index(bank_id) + index = await self._get_and_cache_vector_db_index(vector_db_id) - await index.insert_documents(documents) + await index.insert_chunks(chunks, embeddings) - async def query_documents( + async def query_chunks( self, - bank_id: str, + vector_db_id: str, query: InterleavedContent, params: Optional[Dict[str, Any]] = None, - ) -> QueryDocumentsResponse: - index = await self._get_and_cache_bank_index(bank_id) + ) -> QueryChunksResponse: + index = await self._get_and_cache_vector_db_index(vector_db_id) - return await index.query_documents(query, params) + return await index.query_chunks(query, params) - async def _get_and_cache_bank_index(self, bank_id: str) -> BankWithIndex: - if bank_id in self.cache: - return self.cache[bank_id] + async def _get_and_cache_vector_db_index( + self, vector_db_id: str + ) -> VectorDBWithIndex: + if vector_db_id in self.cache: + return self.cache[vector_db_id] - bank = await self.memory_bank_store.get_memory_bank(bank_id) - if not bank: - raise ValueError(f"Bank {bank_id} not found in Llama Stack") - collection = await maybe_await(self.client.get_collection(bank_id)) + vector_db = await self.vector_db_store.get_vector_db(vector_db_id) + if not vector_db: + raise ValueError(f"Vector DB {vector_db_id} not found in Llama Stack") + collection = await maybe_await(self.client.get_collection(vector_db_id)) if not collection: - raise ValueError(f"Bank {bank_id} not found in Chroma") - index = BankWithIndex( - bank, ChromaIndex(self.client, collection), self.inference_api + raise ValueError(f"Vector DB {vector_db_id} not found in Chroma") + index = VectorDBWithIndex( + vector_db, ChromaIndex(self.client, collection), self.inference_api ) - self.cache[bank_id] = index + self.cache[vector_db_id] = index return index diff --git a/llama_stack/providers/remote/vector_io/pgvector/pgvector.py b/llama_stack/providers/remote/vector_io/pgvector/pgvector.py index b2c720b2c..3605f038c 100644 --- a/llama_stack/providers/remote/vector_io/pgvector/pgvector.py +++ b/llama_stack/providers/remote/vector_io/pgvector/pgvector.py @@ -12,21 +12,16 @@ from numpy.typing import NDArray from psycopg2 import sql from psycopg2.extras import execute_values, Json -from pydantic import BaseModel, parse_obj_as +from pydantic import BaseModel, TypeAdapter from llama_stack.apis.inference import InterleavedContent -from llama_stack.apis.memory import ( - Chunk, - Memory, - MemoryBankDocument, - QueryDocumentsResponse, -) -from llama_stack.apis.memory_banks import MemoryBank, MemoryBankType, VectorMemoryBank -from llama_stack.providers.datatypes import Api, MemoryBanksProtocolPrivate +from llama_stack.apis.vector_dbs import VectorDB +from llama_stack.apis.vector_io import Chunk, QueryChunksResponse, VectorIO +from llama_stack.providers.datatypes import Api, VectorDBsProtocolPrivate from llama_stack.providers.utils.memory.vector_store import ( - BankWithIndex, EmbeddingIndex, + VectorDBWithIndex, ) from .config import PGVectorConfig @@ -50,20 +45,20 @@ def upsert_models(cur, keys_models: List[Tuple[str, BaseModel]]): """ ) - values = [(key, Json(model.dict())) for key, model in keys_models] + values = [(key, Json(model.model_dump())) for key, model in keys_models] execute_values(cur, query, values, template="(%s, %s)") def load_models(cur, cls): cur.execute("SELECT key, data FROM metadata_store") rows = cur.fetchall() - return [parse_obj_as(cls, row["data"]) for row in rows] + return [TypeAdapter(cls).validate_python(row["data"]) for row in rows] class PGVectorIndex(EmbeddingIndex): - def __init__(self, bank: VectorMemoryBank, dimension: int, cursor): + def __init__(self, vector_db: VectorDB, dimension: int, cursor): self.cursor = cursor - self.table_name = f"vector_store_{bank.identifier}" + self.table_name = f"vector_store_{vector_db.identifier}" self.cursor.execute( f""" @@ -85,7 +80,7 @@ class PGVectorIndex(EmbeddingIndex): values.append( ( f"{chunk.document_id}:chunk-{i}", - Json(chunk.dict()), + Json(chunk.model_dump()), embeddings[i].tolist(), ) ) @@ -101,7 +96,7 @@ class PGVectorIndex(EmbeddingIndex): async def query( self, embedding: NDArray, k: int, score_threshold: float - ) -> QueryDocumentsResponse: + ) -> QueryChunksResponse: self.cursor.execute( f""" SELECT document, embedding <-> %s::vector AS distance @@ -119,13 +114,13 @@ class PGVectorIndex(EmbeddingIndex): chunks.append(Chunk(**doc)) scores.append(1.0 / float(dist)) - return QueryDocumentsResponse(chunks=chunks, scores=scores) + return QueryChunksResponse(chunks=chunks, scores=scores) async def delete(self): self.cursor.execute(f"DROP TABLE IF EXISTS {self.table_name}") -class PGVectorMemoryAdapter(Memory, MemoryBanksProtocolPrivate): +class PGVectorVectorDBAdapter(VectorIO, VectorDBsProtocolPrivate): def __init__(self, config: PGVectorConfig, inference_api: Api.inference) -> None: self.config = config self.inference_api = inference_api @@ -167,46 +162,45 @@ class PGVectorMemoryAdapter(Memory, MemoryBanksProtocolPrivate): async def shutdown(self) -> None: pass - async def register_memory_bank(self, memory_bank: MemoryBank) -> None: - assert ( - memory_bank.memory_bank_type == MemoryBankType.vector.value - ), f"Only vector banks are supported {memory_bank.memory_bank_type}" + async def register_vector_db(self, vector_db: VectorDB) -> None: + upsert_models(self.cursor, [(vector_db.identifier, vector_db)]) - upsert_models(self.cursor, [(memory_bank.identifier, memory_bank)]) - index = PGVectorIndex(memory_bank, memory_bank.embedding_dimension, self.cursor) - self.cache[memory_bank.identifier] = BankWithIndex( - memory_bank, index, self.inference_api + index = PGVectorIndex(vector_db, vector_db.embedding_dimension, self.cursor) + self.cache[vector_db.identifier] = VectorDBWithIndex( + vector_db, index, self.inference_api ) - async def unregister_memory_bank(self, memory_bank_id: str) -> None: - await self.cache[memory_bank_id].index.delete() - del self.cache[memory_bank_id] + async def unregister_vector_db(self, vector_db_id: str) -> None: + await self.cache[vector_db_id].index.delete() + del self.cache[vector_db_id] - async def insert_documents( + async def insert_chunks( self, - bank_id: str, - documents: List[MemoryBankDocument], + vector_db_id: str, + chunks: List[Chunk], ttl_seconds: Optional[int] = None, ) -> None: - index = await self._get_and_cache_bank_index(bank_id) - await index.insert_documents(documents) + index = await self._get_and_cache_vector_db_index(vector_db_id) + await index.insert_chunks(chunks) - async def query_documents( + async def query_chunks( self, - bank_id: str, + vector_db_id: str, query: InterleavedContent, params: Optional[Dict[str, Any]] = None, - ) -> QueryDocumentsResponse: - index = await self._get_and_cache_bank_index(bank_id) - return await index.query_documents(query, params) + ) -> QueryChunksResponse: + index = await self._get_and_cache_vector_db_index(vector_db_id) + return await index.query_chunks(query, params) - self.inference_api = inference_api + async def _get_and_cache_vector_db_index( + self, vector_db_id: str + ) -> VectorDBWithIndex: + if vector_db_id in self.cache: + return self.cache[vector_db_id] - async def _get_and_cache_bank_index(self, bank_id: str) -> BankWithIndex: - if bank_id in self.cache: - return self.cache[bank_id] - - bank = await self.memory_bank_store.get_memory_bank(bank_id) - index = PGVectorIndex(bank, bank.embedding_dimension, self.cursor) - self.cache[bank_id] = BankWithIndex(bank, index, self.inference_api) - return self.cache[bank_id] + vector_db = await self.vector_db_store.get_vector_db(vector_db_id) + index = PGVectorIndex(vector_db, vector_db.embedding_dimension, self.cursor) + self.cache[vector_db_id] = VectorDBWithIndex( + vector_db, index, self.inference_api + ) + return self.cache[vector_db_id] diff --git a/llama_stack/providers/remote/vector_io/qdrant/qdrant.py b/llama_stack/providers/remote/vector_io/qdrant/qdrant.py index b1d5bd7fa..d3257b4c9 100644 --- a/llama_stack/providers/remote/vector_io/qdrant/qdrant.py +++ b/llama_stack/providers/remote/vector_io/qdrant/qdrant.py @@ -13,19 +13,14 @@ from qdrant_client import AsyncQdrantClient, models from qdrant_client.models import PointStruct from llama_stack.apis.inference import InterleavedContent -from llama_stack.apis.memory import ( - Chunk, - Memory, - MemoryBankDocument, - QueryDocumentsResponse, -) -from llama_stack.apis.memory_banks import MemoryBank, MemoryBankType -from llama_stack.providers.datatypes import Api, MemoryBanksProtocolPrivate -from llama_stack.providers.remote.memory.qdrant.config import QdrantConfig +from llama_stack.apis.vector_dbs import VectorDB +from llama_stack.apis.vector_io import Chunk, QueryChunksResponse, VectorIO +from llama_stack.providers.datatypes import Api, VectorDBsProtocolPrivate from llama_stack.providers.utils.memory.vector_store import ( - BankWithIndex, EmbeddingIndex, + VectorDBWithIndex, ) +from .config import QdrantConfig log = logging.getLogger(__name__) CHUNK_ID_KEY = "_chunk_id" @@ -76,7 +71,7 @@ class QdrantIndex(EmbeddingIndex): async def query( self, embedding: NDArray, k: int, score_threshold: float - ) -> QueryDocumentsResponse: + ) -> QueryChunksResponse: results = ( await self.client.query_points( collection_name=self.collection_name, @@ -101,10 +96,10 @@ class QdrantIndex(EmbeddingIndex): chunks.append(chunk) scores.append(point.score) - return QueryDocumentsResponse(chunks=chunks, scores=scores) + return QueryChunksResponse(chunks=chunks, scores=scores) -class QdrantVectorMemoryAdapter(Memory, MemoryBanksProtocolPrivate): +class QdrantVectorDBAdapter(VectorIO, VectorDBsProtocolPrivate): def __init__(self, config: QdrantConfig, inference_api: Api.inference) -> None: self.config = config self.client = AsyncQdrantClient(**self.config.model_dump(exclude_none=True)) @@ -117,58 +112,56 @@ class QdrantVectorMemoryAdapter(Memory, MemoryBanksProtocolPrivate): async def shutdown(self) -> None: self.client.close() - async def register_memory_bank( + async def register_vector_db( self, - memory_bank: MemoryBank, + vector_db: VectorDB, ) -> None: - assert ( - memory_bank.memory_bank_type == MemoryBankType.vector - ), f"Only vector banks are supported {memory_bank.memory_bank_type}" - - index = BankWithIndex( - bank=memory_bank, - index=QdrantIndex(self.client, memory_bank.identifier), + index = VectorDBWithIndex( + vector_db=vector_db, + index=QdrantIndex(self.client, vector_db.identifier), inference_api=self.inference_api, ) - self.cache[memory_bank.identifier] = index + self.cache[vector_db.identifier] = index - async def _get_and_cache_bank_index(self, bank_id: str) -> Optional[BankWithIndex]: - if bank_id in self.cache: - return self.cache[bank_id] + async def _get_and_cache_vector_db_index( + self, vector_db_id: str + ) -> Optional[VectorDBWithIndex]: + if vector_db_id in self.cache: + return self.cache[vector_db_id] - bank = await self.memory_bank_store.get_memory_bank(bank_id) - if not bank: - raise ValueError(f"Bank {bank_id} not found") + vector_db = await self.vector_db_store.get_vector_db(vector_db_id) + if not vector_db: + raise ValueError(f"Vector DB {vector_db_id} not found") - index = BankWithIndex( - bank=bank, - index=QdrantIndex(client=self.client, collection_name=bank_id), + index = VectorDBWithIndex( + vector_db=vector_db, + index=QdrantIndex(client=self.client, collection_name=vector_db.identifier), inference_api=self.inference_api, ) - self.cache[bank_id] = index + self.cache[vector_db_id] = index return index - async def insert_documents( + async def insert_chunks( self, - bank_id: str, - documents: List[MemoryBankDocument], + vector_db_id: str, + chunks: List[Chunk], ttl_seconds: Optional[int] = None, ) -> None: - index = await self._get_and_cache_bank_index(bank_id) + index = await self._get_and_cache_vector_db_index(vector_db_id) if not index: - raise ValueError(f"Bank {bank_id} not found") + raise ValueError(f"Vector DB {vector_db_id} not found") - await index.insert_documents(documents) + await index.insert_chunks(chunks) - async def query_documents( + async def query_chunks( self, - bank_id: str, + vector_db_id: str, query: InterleavedContent, params: Optional[Dict[str, Any]] = None, - ) -> QueryDocumentsResponse: - index = await self._get_and_cache_bank_index(bank_id) + ) -> QueryChunksResponse: + index = await self._get_and_cache_vector_db_index(vector_db_id) if not index: - raise ValueError(f"Bank {bank_id} not found") + raise ValueError(f"Vector DB {vector_db_id} not found") - return await index.query_documents(query, params) + return await index.query_chunks(query, params) diff --git a/llama_stack/providers/remote/vector_io/sample/sample.py b/llama_stack/providers/remote/vector_io/sample/sample.py index b051eb544..e311be39d 100644 --- a/llama_stack/providers/remote/vector_io/sample/sample.py +++ b/llama_stack/providers/remote/vector_io/sample/sample.py @@ -4,19 +4,22 @@ # This source code is licensed under the terms described in the LICENSE file in # the root directory of this source tree. -from llama_stack.apis.memory import Memory -from llama_stack.apis.memory_banks import MemoryBank +from llama_stack.apis.vector_dbs import VectorDB +from llama_stack.apis.vector_io import VectorIO from .config import SampleConfig -class SampleMemoryImpl(Memory): +class SampleMemoryImpl(VectorIO): def __init__(self, config: SampleConfig): self.config = config - async def register_memory_bank(self, memory_bank: MemoryBank) -> None: - # these are the memory banks the Llama Stack will use to route requests to this provider + async def register_vector_db(self, vector_db: VectorDB) -> None: + # these are the vector dbs the Llama Stack will use to route requests to this provider # perform validation here if necessary pass async def initialize(self): pass + + async def shutdown(self): + pass diff --git a/llama_stack/providers/remote/vector_io/weaviate/weaviate.py b/llama_stack/providers/remote/vector_io/weaviate/weaviate.py index f1433090d..ea9ce5185 100644 --- a/llama_stack/providers/remote/vector_io/weaviate/weaviate.py +++ b/llama_stack/providers/remote/vector_io/weaviate/weaviate.py @@ -15,18 +15,13 @@ from weaviate.classes.init import Auth from weaviate.classes.query import Filter from llama_stack.apis.common.content_types import InterleavedContent -from llama_stack.apis.memory import ( - Chunk, - Memory, - MemoryBankDocument, - QueryDocumentsResponse, -) -from llama_stack.apis.memory_banks import MemoryBank, MemoryBankType +from llama_stack.apis.vector_dbs import VectorDB +from llama_stack.apis.vector_io import Chunk, QueryChunksResponse, VectorIO from llama_stack.distribution.request_headers import NeedsRequestProviderData -from llama_stack.providers.datatypes import Api, MemoryBanksProtocolPrivate +from llama_stack.providers.datatypes import Api, VectorDBsProtocolPrivate from llama_stack.providers.utils.memory.vector_store import ( - BankWithIndex, EmbeddingIndex, + VectorDBWithIndex, ) from .config import WeaviateConfig, WeaviateRequestProviderData @@ -49,7 +44,7 @@ class WeaviateIndex(EmbeddingIndex): data_objects.append( wvc.data.DataObject( properties={ - "chunk_content": chunk.json(), + "chunk_content": chunk.model_dump_json(), }, vector=embeddings[i].tolist(), ) @@ -63,7 +58,7 @@ class WeaviateIndex(EmbeddingIndex): async def query( self, embedding: NDArray, k: int, score_threshold: float - ) -> QueryDocumentsResponse: + ) -> QueryChunksResponse: collection = self.client.collections.get(self.collection_name) results = collection.query.near_vector( @@ -86,7 +81,7 @@ class WeaviateIndex(EmbeddingIndex): chunks.append(chunk) scores.append(1.0 / doc.metadata.distance) - return QueryDocumentsResponse(chunks=chunks, scores=scores) + return QueryChunksResponse(chunks=chunks, scores=scores) async def delete(self, chunk_ids: List[str]) -> None: collection = self.client.collections.get(self.collection_name) @@ -96,9 +91,9 @@ class WeaviateIndex(EmbeddingIndex): class WeaviateMemoryAdapter( - Memory, + VectorIO, NeedsRequestProviderData, - MemoryBanksProtocolPrivate, + VectorDBsProtocolPrivate, ): def __init__(self, config: WeaviateConfig, inference_api: Api.inference) -> None: self.config = config @@ -129,20 +124,16 @@ class WeaviateMemoryAdapter( for client in self.client_cache.values(): client.close() - async def register_memory_bank( + async def register_vector_db( self, - memory_bank: MemoryBank, + vector_db: VectorDB, ) -> None: - assert ( - memory_bank.memory_bank_type == MemoryBankType.vector.value - ), f"Only vector banks are supported {memory_bank.memory_bank_type}" - client = self._get_client() # Create collection if it doesn't exist - if not client.collections.exists(memory_bank.identifier): + if not client.collections.exists(vector_db.identifier): client.collections.create( - name=memory_bank.identifier, + name=vector_db.identifier, vectorizer_config=wvc.config.Configure.Vectorizer.none(), properties=[ wvc.config.Property( @@ -152,52 +143,54 @@ class WeaviateMemoryAdapter( ], ) - self.cache[memory_bank.identifier] = BankWithIndex( - memory_bank, - WeaviateIndex(client=client, collection_name=memory_bank.identifier), + self.cache[vector_db.identifier] = VectorDBWithIndex( + vector_db, + WeaviateIndex(client=client, collection_name=vector_db.identifier), self.inference_api, ) - async def _get_and_cache_bank_index(self, bank_id: str) -> Optional[BankWithIndex]: - if bank_id in self.cache: - return self.cache[bank_id] + async def _get_and_cache_vector_db_index( + self, vector_db_id: str + ) -> Optional[VectorDBWithIndex]: + if vector_db_id in self.cache: + return self.cache[vector_db_id] - bank = await self.memory_bank_store.get_memory_bank(bank_id) - if not bank: - raise ValueError(f"Bank {bank_id} not found") + vector_db = await self.vector_db_store.get_vector_db(vector_db_id) + if not vector_db: + raise ValueError(f"Vector DB {vector_db_id} not found") client = self._get_client() - if not client.collections.exists(bank.identifier): - raise ValueError(f"Collection with name `{bank.identifier}` not found") + if not client.collections.exists(vector_db.identifier): + raise ValueError(f"Collection with name `{vector_db.identifier}` not found") - index = BankWithIndex( - bank=bank, - index=WeaviateIndex(client=client, collection_name=bank_id), + index = VectorDBWithIndex( + vector_db=vector_db, + index=WeaviateIndex(client=client, collection_name=vector_db.identifier), inference_api=self.inference_api, ) - self.cache[bank_id] = index + self.cache[vector_db_id] = index return index - async def insert_documents( + async def insert_chunks( self, - bank_id: str, - documents: List[MemoryBankDocument], + vector_db_id: str, + chunks: List[Chunk], ttl_seconds: Optional[int] = None, ) -> None: - index = await self._get_and_cache_bank_index(bank_id) + index = await self._get_and_cache_vector_db_index(vector_db_id) if not index: - raise ValueError(f"Bank {bank_id} not found") + raise ValueError(f"Vector DB {vector_db_id} not found") - await index.insert_documents(documents) + await index.insert_chunks(chunks) - async def query_documents( + async def query_chunks( self, - bank_id: str, + vector_db_id: str, query: InterleavedContent, params: Optional[Dict[str, Any]] = None, - ) -> QueryDocumentsResponse: - index = await self._get_and_cache_bank_index(bank_id) + ) -> QueryChunksResponse: + index = await self._get_and_cache_vector_db_index(vector_db_id) if not index: - raise ValueError(f"Bank {bank_id} not found") + raise ValueError(f"Vector DB {vector_db_id} not found") - return await index.query_documents(query, params) + return await index.query_chunks(query, params) diff --git a/llama_stack/providers/tests/eval/fixtures.py b/llama_stack/providers/tests/eval/fixtures.py index 37bb0527a..009e65fb3 100644 --- a/llama_stack/providers/tests/eval/fixtures.py +++ b/llama_stack/providers/tests/eval/fixtures.py @@ -53,7 +53,7 @@ async def eval_stack( "inference", "agents", "safety", - "memory", + "vector_io", "tool_runtime", ]: fixture = request.getfixturevalue(f"{key}_{fixture_dict[key]}") @@ -69,7 +69,7 @@ async def eval_stack( Api.scoring, Api.agents, Api.safety, - Api.memory, + Api.vector_io, Api.tool_runtime, ], providers, diff --git a/llama_stack/providers/tests/tools/fixtures.py b/llama_stack/providers/tests/tools/fixtures.py index a559dbf8c..03752881a 100644 --- a/llama_stack/providers/tests/tools/fixtures.py +++ b/llama_stack/providers/tests/tools/fixtures.py @@ -83,7 +83,7 @@ async def tools_stack( providers = {} provider_data = {} - for key in ["inference", "memory", "tool_runtime"]: + for key in ["inference", "vector_io", "tool_runtime"]: fixture = request.getfixturevalue(f"{key}_{fixture_dict[key]}") providers[key] = fixture.providers if key == "inference": @@ -117,7 +117,12 @@ async def tools_stack( ) test_stack = await construct_stack_for_test( - [Api.tool_groups, Api.inference, Api.memory, Api.tool_runtime], + [ + Api.tool_groups, + Api.inference, + Api.vector_io, + Api.tool_runtime, + ], providers, provider_data, models=models, diff --git a/llama_stack/providers/tests/tools/test_tools.py b/llama_stack/providers/tests/tools/test_tools.py index 16081b939..62b18ea66 100644 --- a/llama_stack/providers/tests/tools/test_tools.py +++ b/llama_stack/providers/tests/tools/test_tools.py @@ -8,10 +8,7 @@ import os import pytest -from llama_stack.apis.inference import UserMessage -from llama_stack.apis.memory import MemoryBankDocument -from llama_stack.apis.memory_banks import VectorMemoryBankParams -from llama_stack.apis.tools import ToolInvocationResult +from llama_stack.apis.tools import RAGDocument, RAGQueryResult, ToolInvocationResult from llama_stack.providers.datatypes import Api @@ -36,7 +33,7 @@ def sample_documents(): "lora_finetune.rst", ] return [ - MemoryBankDocument( + RAGDocument( document_id=f"num-{i}", content=f"https://raw.githubusercontent.com/pytorch/torchtune/main/docs/source/tutorials/{url}", mime_type="text/plain", @@ -57,7 +54,7 @@ class TestTools: # Execute the tool response = await tools_impl.invoke_tool( - tool_name="web_search", args={"query": sample_search_query} + tool_name="web_search", kwargs={"query": sample_search_query} ) # Verify the response @@ -75,7 +72,7 @@ class TestTools: tools_impl = tools_stack.impls[Api.tool_runtime] response = await tools_impl.invoke_tool( - tool_name="wolfram_alpha", args={"query": sample_wolfram_alpha_query} + tool_name="wolfram_alpha", kwargs={"query": sample_wolfram_alpha_query} ) # Verify the response @@ -85,43 +82,33 @@ class TestTools: assert isinstance(response.content, str) @pytest.mark.asyncio - async def test_memory_tool(self, tools_stack, sample_documents): + async def test_rag_tool(self, tools_stack, sample_documents): """Test the memory tool functionality.""" - memory_banks_impl = tools_stack.impls[Api.memory_banks] - memory_impl = tools_stack.impls[Api.memory] + vector_dbs_impl = tools_stack.impls[Api.vector_dbs] tools_impl = tools_stack.impls[Api.tool_runtime] # Register memory bank - await memory_banks_impl.register_memory_bank( - memory_bank_id="test_bank", - params=VectorMemoryBankParams( - embedding_model="all-MiniLM-L6-v2", - chunk_size_in_tokens=512, - overlap_size_in_tokens=64, - ), + await vector_dbs_impl.register( + vector_db_id="test_bank", + embedding_model="all-MiniLM-L6-v2", + embedding_dimension=384, provider_id="faiss", ) # Insert documents into memory - await memory_impl.insert_documents( - bank_id="test_bank", + await tools_impl.rag_tool.insert_documents( documents=sample_documents, + vector_db_id="test_bank", + chunk_size_in_tokens=512, ) # Execute the memory tool - response = await tools_impl.invoke_tool( - tool_name="memory", - args={ - "messages": [ - UserMessage( - content="What are the main topics covered in the documentation?", - ) - ], - "memory_bank_ids": ["test_bank"], - }, + response = await tools_impl.rag_tool.query_context( + content="What are the main topics covered in the documentation?", + vector_db_ids=["test_bank"], ) # Verify the response - assert isinstance(response, ToolInvocationResult) + assert isinstance(response, RAGQueryResult) assert response.content is not None assert len(response.content) > 0 diff --git a/llama_stack/templates/bedrock/bedrock.py b/llama_stack/templates/bedrock/bedrock.py index 668134be8..20f670891 100644 --- a/llama_stack/templates/bedrock/bedrock.py +++ b/llama_stack/templates/bedrock/bedrock.py @@ -10,7 +10,7 @@ from llama_models.sku_list import all_registered_models from llama_stack.apis.models import ModelInput from llama_stack.distribution.datatypes import Provider, ToolGroupInput -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.bedrock.bedrock import MODEL_ALIASES from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -18,7 +18,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::bedrock"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["remote::bedrock"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -34,7 +34,7 @@ def get_distribution_template() -> DistributionTemplate: ], } name = "bedrock" - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -78,7 +78,7 @@ def get_distribution_template() -> DistributionTemplate: run_configs={ "run.yaml": RunConfigSettings( provider_overrides={ - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=default_models, default_tool_groups=default_tool_groups, diff --git a/llama_stack/templates/bedrock/build.yaml b/llama_stack/templates/bedrock/build.yaml index 95b8684e3..9ae11e9bb 100644 --- a/llama_stack/templates/bedrock/build.yaml +++ b/llama_stack/templates/bedrock/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::bedrock - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/bedrock/run.yaml b/llama_stack/templates/bedrock/run.yaml index 118723bbc..577263bbf 100644 --- a/llama_stack/templates/bedrock/run.yaml +++ b/llama_stack/templates/bedrock/run.yaml @@ -5,17 +5,17 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: bedrock provider_type: remote::bedrock config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -104,7 +104,7 @@ models: provider_model_id: meta.llama3-1-405b-instruct-v1:0 model_type: llm shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/cerebras/build.yaml b/llama_stack/templates/cerebras/build.yaml index 9f187d3c7..6d43ed0ca 100644 --- a/llama_stack/templates/cerebras/build.yaml +++ b/llama_stack/templates/cerebras/build.yaml @@ -6,7 +6,7 @@ distribution_spec: - remote::cerebras safety: - inline::llama-guard - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/cerebras/cerebras.py b/llama_stack/templates/cerebras/cerebras.py index 8f6bd77af..be51e635d 100644 --- a/llama_stack/templates/cerebras/cerebras.py +++ b/llama_stack/templates/cerebras/cerebras.py @@ -13,7 +13,7 @@ from llama_stack.distribution.datatypes import ModelInput, Provider, ToolGroupIn from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.cerebras import CerebrasImplConfig from llama_stack.providers.remote.inference.cerebras.cerebras import model_aliases from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -23,7 +23,7 @@ def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::cerebras"], "safety": ["inline::llama-guard"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "agents": ["inline::meta-reference"], "eval": ["inline::meta-reference"], "datasetio": ["remote::huggingface", "inline::localfs"], @@ -68,7 +68,7 @@ def get_distribution_template() -> DistributionTemplate: "embedding_dimension": 384, }, ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -100,7 +100,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=default_models + [embedding_model], default_shields=[], diff --git a/llama_stack/templates/cerebras/run.yaml b/llama_stack/templates/cerebras/run.yaml index bfc492bda..0553f0749 100644 --- a/llama_stack/templates/cerebras/run.yaml +++ b/llama_stack/templates/cerebras/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: cerebras @@ -24,7 +24,7 @@ providers: - provider_id: llama-guard provider_type: inline::llama-guard config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -106,7 +106,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/experimental-post-training/run.yaml b/llama_stack/templates/experimental-post-training/run.yaml index 87465137f..14323573c 100644 --- a/llama_stack/templates/experimental-post-training/run.yaml +++ b/llama_stack/templates/experimental-post-training/run.yaml @@ -60,7 +60,7 @@ providers: - provider_id: llama-guard provider_type: inline::llama-guard config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -82,7 +82,7 @@ metadata_store: db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/meta-reference-gpu}/registry.db models: [] shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/fireworks/build.yaml b/llama_stack/templates/fireworks/build.yaml index d8e1e27ee..7e19cd5e6 100644 --- a/llama_stack/templates/fireworks/build.yaml +++ b/llama_stack/templates/fireworks/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::fireworks - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/fireworks/fireworks.py b/llama_stack/templates/fireworks/fireworks.py index 14fd392c4..5f1b9e8a0 100644 --- a/llama_stack/templates/fireworks/fireworks.py +++ b/llama_stack/templates/fireworks/fireworks.py @@ -18,7 +18,7 @@ from llama_stack.distribution.datatypes import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.fireworks import FireworksImplConfig from llama_stack.providers.remote.inference.fireworks.fireworks import MODEL_ALIASES from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -27,7 +27,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::fireworks"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -55,7 +55,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::sentence-transformers", config=SentenceTransformersInferenceConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -107,7 +107,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=default_models + [embedding_model], default_shields=[ShieldInput(shield_id="meta-llama/Llama-Guard-3-8B")], @@ -119,7 +119,7 @@ def get_distribution_template() -> DistributionTemplate: inference_provider, embedding_provider, ], - "memory": [memory_provider], + "vector_io": [vector_io_provider], "safety": [ Provider( provider_id="llama-guard", diff --git a/llama_stack/templates/fireworks/run-with-safety.yaml b/llama_stack/templates/fireworks/run-with-safety.yaml index dd21120ed..659ec5191 100644 --- a/llama_stack/templates/fireworks/run-with-safety.yaml +++ b/llama_stack/templates/fireworks/run-with-safety.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: fireworks @@ -20,7 +20,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -161,7 +161,7 @@ shields: provider_id: llama-guard-vision - shield_id: CodeScanner provider_id: code-scanner -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/fireworks/run.yaml b/llama_stack/templates/fireworks/run.yaml index 993417b50..9fb61f842 100644 --- a/llama_stack/templates/fireworks/run.yaml +++ b/llama_stack/templates/fireworks/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: fireworks @@ -20,7 +20,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -150,7 +150,7 @@ models: model_type: embedding shields: - shield_id: meta-llama/Llama-Guard-3-8B -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/hf-endpoint/build.yaml b/llama_stack/templates/hf-endpoint/build.yaml index f4fdc4a3d..82a460bd9 100644 --- a/llama_stack/templates/hf-endpoint/build.yaml +++ b/llama_stack/templates/hf-endpoint/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::hf::endpoint - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/hf-endpoint/hf_endpoint.py b/llama_stack/templates/hf-endpoint/hf_endpoint.py index 1a5c23a42..f9bfe85f9 100644 --- a/llama_stack/templates/hf-endpoint/hf_endpoint.py +++ b/llama_stack/templates/hf-endpoint/hf_endpoint.py @@ -14,7 +14,7 @@ from llama_stack.distribution.datatypes import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.tgi import InferenceEndpointImplConfig from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -22,7 +22,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::hf::endpoint"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -48,7 +48,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::sentence-transformers", config=SentenceTransformersInferenceConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -97,7 +97,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[inference_model, embedding_model], default_tool_groups=default_tool_groups, @@ -115,7 +115,7 @@ def get_distribution_template() -> DistributionTemplate: ), ), ], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[ inference_model, diff --git a/llama_stack/templates/hf-endpoint/run-with-safety.yaml b/llama_stack/templates/hf-endpoint/run-with-safety.yaml index 537e4024f..dfa094fe6 100644 --- a/llama_stack/templates/hf-endpoint/run-with-safety.yaml +++ b/llama_stack/templates/hf-endpoint/run-with-safety.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: hf-endpoint @@ -25,7 +25,7 @@ providers: config: endpoint_name: ${env.SAFETY_INFERENCE_ENDPOINT_NAME} api_token: ${env.HF_API_TOKEN} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -113,7 +113,7 @@ models: model_type: embedding shields: - shield_id: ${env.SAFETY_MODEL} -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/hf-endpoint/run.yaml b/llama_stack/templates/hf-endpoint/run.yaml index b31f28434..fb5d7fa31 100644 --- a/llama_stack/templates/hf-endpoint/run.yaml +++ b/llama_stack/templates/hf-endpoint/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: hf-endpoint @@ -20,7 +20,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -103,7 +103,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/hf-serverless/build.yaml b/llama_stack/templates/hf-serverless/build.yaml index d075a7449..0eb4e0509 100644 --- a/llama_stack/templates/hf-serverless/build.yaml +++ b/llama_stack/templates/hf-serverless/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::hf::serverless - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/hf-serverless/hf_serverless.py b/llama_stack/templates/hf-serverless/hf_serverless.py index 0292f13e2..4f3c29404 100644 --- a/llama_stack/templates/hf-serverless/hf_serverless.py +++ b/llama_stack/templates/hf-serverless/hf_serverless.py @@ -14,7 +14,7 @@ from llama_stack.distribution.datatypes import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.tgi import InferenceAPIImplConfig from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -22,7 +22,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::hf::serverless"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -49,7 +49,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::sentence-transformers", config=SentenceTransformersInferenceConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -98,7 +98,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[inference_model, embedding_model], default_tool_groups=default_tool_groups, @@ -116,7 +116,7 @@ def get_distribution_template() -> DistributionTemplate: ), ), ], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[ inference_model, diff --git a/llama_stack/templates/hf-serverless/run-with-safety.yaml b/llama_stack/templates/hf-serverless/run-with-safety.yaml index 484b2d0bd..0575efaef 100644 --- a/llama_stack/templates/hf-serverless/run-with-safety.yaml +++ b/llama_stack/templates/hf-serverless/run-with-safety.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: hf-serverless @@ -25,7 +25,7 @@ providers: config: huggingface_repo: ${env.SAFETY_MODEL} api_token: ${env.HF_API_TOKEN} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -113,7 +113,7 @@ models: model_type: embedding shields: - shield_id: ${env.SAFETY_MODEL} -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/hf-serverless/run.yaml b/llama_stack/templates/hf-serverless/run.yaml index a75baf1f9..b87edd744 100644 --- a/llama_stack/templates/hf-serverless/run.yaml +++ b/llama_stack/templates/hf-serverless/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: hf-serverless @@ -20,7 +20,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -103,7 +103,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/meta-reference-gpu/build.yaml b/llama_stack/templates/meta-reference-gpu/build.yaml index a75d3604b..f5371f0d6 100644 --- a/llama_stack/templates/meta-reference-gpu/build.yaml +++ b/llama_stack/templates/meta-reference-gpu/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - inline::meta-reference - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/meta-reference-gpu/meta_reference.py b/llama_stack/templates/meta-reference-gpu/meta_reference.py index 584d38256..dae4f0218 100644 --- a/llama_stack/templates/meta-reference-gpu/meta_reference.py +++ b/llama_stack/templates/meta-reference-gpu/meta_reference.py @@ -19,14 +19,14 @@ from llama_stack.providers.inline.inference.meta_reference import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.templates.template import DistributionTemplate, RunConfigSettings def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["inline::meta-reference"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -55,7 +55,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::sentence-transformers", config=SentenceTransformersInferenceConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -103,7 +103,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[inference_model, embedding_model], default_tool_groups=default_tool_groups, @@ -122,7 +122,7 @@ def get_distribution_template() -> DistributionTemplate: ), ), ], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[ inference_model, diff --git a/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml b/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml index 9dbdb6fa5..54ddef155 100644 --- a/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml +++ b/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: meta-reference-inference @@ -27,7 +27,7 @@ providers: model: ${env.SAFETY_MODEL} max_seq_len: 4096 checkpoint_dir: ${env.SAFETY_CHECKPOINT_DIR:null} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -115,7 +115,7 @@ models: model_type: embedding shields: - shield_id: ${env.SAFETY_MODEL} -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/meta-reference-gpu/run.yaml b/llama_stack/templates/meta-reference-gpu/run.yaml index 6465215f0..cde581d19 100644 --- a/llama_stack/templates/meta-reference-gpu/run.yaml +++ b/llama_stack/templates/meta-reference-gpu/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: meta-reference-inference @@ -21,7 +21,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -104,7 +104,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/meta-reference-quantized-gpu/build.yaml b/llama_stack/templates/meta-reference-quantized-gpu/build.yaml index 4c3e2f492..aa23ad313 100644 --- a/llama_stack/templates/meta-reference-quantized-gpu/build.yaml +++ b/llama_stack/templates/meta-reference-quantized-gpu/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - inline::meta-reference-quantized - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py b/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py index 56293f42c..4e9cbf1fe 100644 --- a/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py +++ b/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py @@ -14,14 +14,14 @@ from llama_stack.providers.inline.inference.meta_reference import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.templates.template import DistributionTemplate, RunConfigSettings def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["inline::meta-reference-quantized"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -64,7 +64,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::sentence-transformers", config=SentenceTransformersInferenceConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -93,7 +93,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[inference_model, embedding_model], default_tool_groups=default_tool_groups, diff --git a/llama_stack/templates/meta-reference-quantized-gpu/run.yaml b/llama_stack/templates/meta-reference-quantized-gpu/run.yaml index 059034741..cc5793f8f 100644 --- a/llama_stack/templates/meta-reference-quantized-gpu/run.yaml +++ b/llama_stack/templates/meta-reference-quantized-gpu/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: meta-reference-inference @@ -23,7 +23,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -106,7 +106,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/nvidia/build.yaml b/llama_stack/templates/nvidia/build.yaml index 7bd2a3865..d6a510e2e 100644 --- a/llama_stack/templates/nvidia/build.yaml +++ b/llama_stack/templates/nvidia/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::nvidia - memory: + vector_io: - inline::faiss safety: - inline::llama-guard diff --git a/llama_stack/templates/nvidia/nvidia.py b/llama_stack/templates/nvidia/nvidia.py index e72fe359f..5693ba12d 100644 --- a/llama_stack/templates/nvidia/nvidia.py +++ b/llama_stack/templates/nvidia/nvidia.py @@ -17,7 +17,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::nvidia"], - "memory": ["inline::faiss"], + "vector_io": ["inline::faiss"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], diff --git a/llama_stack/templates/nvidia/run.yaml b/llama_stack/templates/nvidia/run.yaml index 07c901371..317aa1031 100644 --- a/llama_stack/templates/nvidia/run.yaml +++ b/llama_stack/templates/nvidia/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: nvidia @@ -17,7 +17,7 @@ providers: config: url: https://integrate.api.nvidia.com api_key: ${env.NVIDIA_API_KEY} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -136,7 +136,7 @@ models: provider_model_id: meta/llama-3.2-90b-vision-instruct model_type: llm shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/ollama/build.yaml b/llama_stack/templates/ollama/build.yaml index 5f2e010ee..c3ed88fb8 100644 --- a/llama_stack/templates/ollama/build.yaml +++ b/llama_stack/templates/ollama/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::ollama - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/ollama/ollama.py b/llama_stack/templates/ollama/ollama.py index 2288ea3a6..bdbd1e142 100644 --- a/llama_stack/templates/ollama/ollama.py +++ b/llama_stack/templates/ollama/ollama.py @@ -16,7 +16,7 @@ from llama_stack.distribution.datatypes import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.ollama import OllamaImplConfig from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -24,7 +24,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::ollama"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -49,7 +49,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::sentence-transformers", config=SentenceTransformersInferenceConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -98,7 +98,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[inference_model, embedding_model], default_tool_groups=default_tool_groups, @@ -109,7 +109,7 @@ def get_distribution_template() -> DistributionTemplate: inference_provider, embedding_provider, ], - "memory": [memory_provider], + "vector_io": [vector_io_provider], "safety": [ Provider( provider_id="llama-guard", diff --git a/llama_stack/templates/ollama/run-with-safety.yaml b/llama_stack/templates/ollama/run-with-safety.yaml index a808590c3..afb0b1938 100644 --- a/llama_stack/templates/ollama/run-with-safety.yaml +++ b/llama_stack/templates/ollama/run-with-safety.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: ollama @@ -19,7 +19,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -110,7 +110,7 @@ shields: provider_id: llama-guard - shield_id: CodeScanner provider_id: code-scanner -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/ollama/run.yaml b/llama_stack/templates/ollama/run.yaml index 2c69296fc..976068670 100644 --- a/llama_stack/templates/ollama/run.yaml +++ b/llama_stack/templates/ollama/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: ollama @@ -19,7 +19,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -99,7 +99,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/remote-vllm/build.yaml b/llama_stack/templates/remote-vllm/build.yaml index 6f301914c..409b2ba10 100644 --- a/llama_stack/templates/remote-vllm/build.yaml +++ b/llama_stack/templates/remote-vllm/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::vllm - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/remote-vllm/run-with-safety.yaml b/llama_stack/templates/remote-vllm/run-with-safety.yaml index 5e5bd6af6..e26d0f99f 100644 --- a/llama_stack/templates/remote-vllm/run-with-safety.yaml +++ b/llama_stack/templates/remote-vllm/run-with-safety.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: vllm-inference @@ -27,7 +27,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -115,7 +115,7 @@ models: model_type: embedding shields: - shield_id: ${env.SAFETY_MODEL} -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/remote-vllm/run.yaml b/llama_stack/templates/remote-vllm/run.yaml index 4eac4dad7..dc54d216d 100644 --- a/llama_stack/templates/remote-vllm/run.yaml +++ b/llama_stack/templates/remote-vllm/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: vllm-inference @@ -21,7 +21,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -104,7 +104,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/remote-vllm/vllm.py b/llama_stack/templates/remote-vllm/vllm.py index 296e2b4f5..f91ad24a7 100644 --- a/llama_stack/templates/remote-vllm/vllm.py +++ b/llama_stack/templates/remote-vllm/vllm.py @@ -16,7 +16,7 @@ from llama_stack.distribution.datatypes import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.vllm import VLLMInferenceAdapterConfig from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -24,7 +24,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::vllm"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "eval": ["inline::meta-reference"], @@ -52,7 +52,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::sentence-transformers", config=SentenceTransformersInferenceConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -100,7 +100,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[inference_model, embedding_model], default_tool_groups=default_tool_groups, @@ -118,7 +118,7 @@ def get_distribution_template() -> DistributionTemplate: ), embedding_provider, ], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[ inference_model, diff --git a/llama_stack/templates/tgi/build.yaml b/llama_stack/templates/tgi/build.yaml index 4391ddd5d..bc31ef7e7 100644 --- a/llama_stack/templates/tgi/build.yaml +++ b/llama_stack/templates/tgi/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - remote::tgi - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/tgi/run-with-safety.yaml b/llama_stack/templates/tgi/run-with-safety.yaml index 9bd06d650..ea8057137 100644 --- a/llama_stack/templates/tgi/run-with-safety.yaml +++ b/llama_stack/templates/tgi/run-with-safety.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: tgi-inference @@ -20,7 +20,7 @@ providers: provider_type: remote::tgi config: url: ${env.TGI_SAFETY_URL} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -103,7 +103,7 @@ models: model_type: llm shields: - shield_id: ${env.SAFETY_MODEL} -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/tgi/run.yaml b/llama_stack/templates/tgi/run.yaml index 2fc1b52d9..d537d0fce 100644 --- a/llama_stack/templates/tgi/run.yaml +++ b/llama_stack/templates/tgi/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: tgi-inference @@ -19,7 +19,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -102,7 +102,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/tgi/tgi.py b/llama_stack/templates/tgi/tgi.py index 8ad9725e3..230fcac2a 100644 --- a/llama_stack/templates/tgi/tgi.py +++ b/llama_stack/templates/tgi/tgi.py @@ -16,7 +16,7 @@ from llama_stack.distribution.datatypes import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.tgi import TGIImplConfig from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -24,7 +24,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::tgi"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -52,7 +52,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::sentence-transformers", config=SentenceTransformersInferenceConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -101,7 +101,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[inference_model, embedding_model], default_tool_groups=default_tool_groups, @@ -118,7 +118,7 @@ def get_distribution_template() -> DistributionTemplate: ), ), ], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[ inference_model, diff --git a/llama_stack/templates/together/run-with-safety.yaml b/llama_stack/templates/together/run-with-safety.yaml index c1461d75d..54b918eea 100644 --- a/llama_stack/templates/together/run-with-safety.yaml +++ b/llama_stack/templates/together/run-with-safety.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: together @@ -20,7 +20,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -156,7 +156,7 @@ shields: provider_id: llama-guard-vision - shield_id: CodeScanner provider_id: code-scanner -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/together/run.yaml b/llama_stack/templates/together/run.yaml index 135b124e4..2c0475796 100644 --- a/llama_stack/templates/together/run.yaml +++ b/llama_stack/templates/together/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- vector_io - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: together @@ -145,6 +145,7 @@ models: model_type: embedding shields: - shield_id: meta-llama/Llama-Guard-3-8B +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/together/together.py b/llama_stack/templates/together/together.py index 1e2def3bd..ec64527d2 100644 --- a/llama_stack/templates/together/together.py +++ b/llama_stack/templates/together/together.py @@ -18,7 +18,7 @@ from llama_stack.distribution.datatypes import ( from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.providers.remote.inference.together import TogetherImplConfig from llama_stack.providers.remote.inference.together.together import MODEL_ALIASES from llama_stack.templates.template import DistributionTemplate, RunConfigSettings @@ -27,7 +27,7 @@ from llama_stack.templates.template import DistributionTemplate, RunConfigSettin def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["remote::together"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -48,7 +48,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="remote::together", config=TogetherImplConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -105,7 +105,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=default_models + [embedding_model], default_tool_groups=default_tool_groups, @@ -117,7 +117,7 @@ def get_distribution_template() -> DistributionTemplate: inference_provider, embedding_provider, ], - "memory": [memory_provider], + "vector_io": [vector_io_provider], "safety": [ Provider( provider_id="llama-guard", diff --git a/llama_stack/templates/vllm-gpu/build.yaml b/llama_stack/templates/vllm-gpu/build.yaml index e8a1693d0..45f543071 100644 --- a/llama_stack/templates/vllm-gpu/build.yaml +++ b/llama_stack/templates/vllm-gpu/build.yaml @@ -4,7 +4,7 @@ distribution_spec: providers: inference: - inline::vllm - memory: + vector_io: - inline::faiss - remote::chromadb - remote::pgvector diff --git a/llama_stack/templates/vllm-gpu/run.yaml b/llama_stack/templates/vllm-gpu/run.yaml index cc0ff047f..2d9ec6a3f 100644 --- a/llama_stack/templates/vllm-gpu/run.yaml +++ b/llama_stack/templates/vllm-gpu/run.yaml @@ -5,11 +5,11 @@ apis: - datasetio - eval - inference -- memory - safety - scoring - telemetry - tool_runtime +- vector_io providers: inference: - provider_id: vllm @@ -23,7 +23,7 @@ providers: - provider_id: sentence-transformers provider_type: inline::sentence-transformers config: {} - memory: + vector_io: - provider_id: faiss provider_type: inline::faiss config: @@ -106,7 +106,7 @@ models: provider_id: sentence-transformers model_type: embedding shields: [] -memory_banks: [] +vector_dbs: [] datasets: [] scoring_fns: [] eval_tasks: [] diff --git a/llama_stack/templates/vllm-gpu/vllm.py b/llama_stack/templates/vllm-gpu/vllm.py index 71b24482d..a8f13ce40 100644 --- a/llama_stack/templates/vllm-gpu/vllm.py +++ b/llama_stack/templates/vllm-gpu/vllm.py @@ -10,7 +10,7 @@ from llama_stack.providers.inline.inference.sentence_transformers import ( SentenceTransformersInferenceConfig, ) from llama_stack.providers.inline.inference.vllm import VLLMConfig -from llama_stack.providers.inline.memory.faiss.config import FaissImplConfig +from llama_stack.providers.inline.vector_io.faiss.config import FaissImplConfig from llama_stack.templates.template import ( DistributionTemplate, RunConfigSettings, @@ -21,7 +21,7 @@ from llama_stack.templates.template import ( def get_distribution_template() -> DistributionTemplate: providers = { "inference": ["inline::vllm"], - "memory": ["inline::faiss", "remote::chromadb", "remote::pgvector"], + "vector_io": ["inline::faiss", "remote::chromadb", "remote::pgvector"], "safety": ["inline::llama-guard"], "agents": ["inline::meta-reference"], "telemetry": ["inline::meta-reference"], @@ -43,7 +43,7 @@ def get_distribution_template() -> DistributionTemplate: provider_type="inline::vllm", config=VLLMConfig.sample_run_config(), ) - memory_provider = Provider( + vector_io_provider = Provider( provider_id="faiss", provider_type="inline::faiss", config=FaissImplConfig.sample_run_config(f"distributions/{name}"), @@ -93,7 +93,7 @@ def get_distribution_template() -> DistributionTemplate: "run.yaml": RunConfigSettings( provider_overrides={ "inference": [inference_provider, embedding_provider], - "memory": [memory_provider], + "vector_io": [vector_io_provider], }, default_models=[inference_model, embedding_model], default_tool_groups=default_tool_groups, From a63a43c646e08e7e98487d1769901db2a464570a Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 10:39:13 -0800 Subject: [PATCH 074/107] [memory refactor][6/n] Update naming and routes (#839) Making a few small naming changes as per feedback: - RAGToolRuntime methods are called `insert` and `query` to keep them more general - The tool names are changed to non-namespaced forms `insert_into_memory` and `query_from_memory` - The REST endpoints are more REST-ful --- docs/resources/llama-stack-spec.html | 532 +++++++++--------- docs/resources/llama-stack-spec.yaml | 35 +- llama_stack/apis/tools/rag_tool.py | 10 +- llama_stack/apis/vector_io/vector_io.py | 2 +- llama_stack/distribution/routers/routers.py | 19 +- .../agents/meta_reference/agent_instance.py | 8 +- .../inline/tool_runtime/memory/memory.py | 11 +- .../providers/tests/tools/test_tools.py | 4 +- .../tests/vector_io/test_vector_store.py | 20 +- tests/client-sdk/agents/test_agents.py | 4 +- .../client-sdk/tool_runtime/test_rag_tool.py | 4 +- 11 files changed, 319 insertions(+), 330 deletions(-) diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index f00d7b291..f6dd1c8dc 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -1887,6 +1887,49 @@ ] } }, + "/v1/tool-runtime/rag-tool/insert": { + "post": { + "responses": { + "200": { + "description": "OK" + } + }, + "tags": [ + "ToolRuntime" + ], + "summary": "Index documents so they can be used by the RAG system", + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InsertRequest" + } + } + }, + "required": true + } + } + }, "/v1/vector-io/insert": { "post": { "responses": { @@ -1929,49 +1972,6 @@ } } }, - "/v1/tool-runtime/rag-tool/insert-documents": { - "post": { - "responses": { - "200": { - "description": "OK" - } - }, - "tags": [ - "ToolRuntime" - ], - "summary": "Index documents so they can be used by the RAG system", - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/InsertDocumentsRequest" - } - } - }, - "required": true - } - } - }, "/v1/tool-runtime/invoke": { "post": { "responses": { @@ -3033,6 +3033,56 @@ } } }, + "/v1/tool-runtime/rag-tool/query": { + "post": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RAGQueryResult" + } + } + } + } + }, + "tags": [ + "ToolRuntime" + ], + "summary": "Query the RAG system for context; typically invoked by the agent", + "parameters": [ + { + "name": "X-LlamaStack-Provider-Data", + "in": "header", + "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "X-LlamaStack-Client-Version", + "in": "header", + "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QueryRequest" + } + } + }, + "required": true + } + } + }, "/v1/vector-io/query": { "post": { "responses": { @@ -3082,56 +3132,6 @@ } } }, - "/v1/tool-runtime/rag-tool/query-context": { - "post": { - "responses": { - "200": { - "description": "OK", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RAGQueryResult" - } - } - } - } - }, - "tags": [ - "ToolRuntime" - ], - "summary": "Query the RAG system for context; typically invoked by the agent", - "parameters": [ - { - "name": "X-LlamaStack-Provider-Data", - "in": "header", - "description": "JSON-encoded provider data which will be made available to the adapter servicing the API", - "required": false, - "schema": { - "type": "string" - } - }, - { - "name": "X-LlamaStack-Client-Version", - "in": "header", - "description": "Version of the client making the request. This is used to ensure that the client and server are compatible.", - "required": false, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/QueryContextRequest" - } - } - }, - "required": true - } - } - }, "/v1/telemetry/spans": { "get": { "responses": { @@ -5256,11 +5256,8 @@ "const": "memory_retrieval", "default": "memory_retrieval" }, - "memory_bank_ids": { - "type": "array", - "items": { - "type": "string" - } + "vector_db_ids": { + "type": "string" }, "inserted_context": { "$ref": "#/components/schemas/InterleavedContent" @@ -5271,7 +5268,7 @@ "turn_id", "step_id", "step_type", - "memory_bank_ids", + "vector_db_ids", "inserted_context" ] }, @@ -6976,63 +6973,6 @@ "status" ] }, - "InsertChunksRequest": { - "type": "object", - "properties": { - "vector_db_id": { - "type": "string" - }, - "chunks": { - "type": "array", - "items": { - "type": "object", - "properties": { - "content": { - "$ref": "#/components/schemas/InterleavedContent" - }, - "metadata": { - "type": "object", - "additionalProperties": { - "oneOf": [ - { - "type": "null" - }, - { - "type": "boolean" - }, - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "array" - }, - { - "type": "object" - } - ] - } - } - }, - "additionalProperties": false, - "required": [ - "content", - "metadata" - ] - } - }, - "ttl_seconds": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "vector_db_id", - "chunks" - ] - }, "RAGDocument": { "type": "object", "properties": { @@ -7094,7 +7034,7 @@ "metadata" ] }, - "InsertDocumentsRequest": { + "InsertRequest": { "type": "object", "properties": { "documents": { @@ -7117,6 +7057,63 @@ "chunk_size_in_tokens" ] }, + "InsertChunksRequest": { + "type": "object", + "properties": { + "vector_db_id": { + "type": "string" + }, + "chunks": { + "type": "array", + "items": { + "type": "object", + "properties": { + "content": { + "$ref": "#/components/schemas/InterleavedContent" + }, + "metadata": { + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "array" + }, + { + "type": "object" + } + ] + } + } + }, + "additionalProperties": false, + "required": [ + "content", + "metadata" + ] + } + }, + "ttl_seconds": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "vector_db_id", + "chunks" + ] + }, "InvokeToolRequest": { "type": "object", "properties": { @@ -7883,6 +7880,110 @@ "job_uuid" ] }, + "DefaultRAGQueryGeneratorConfig": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "default", + "default": "default" + }, + "separator": { + "type": "string", + "default": " " + } + }, + "additionalProperties": false, + "required": [ + "type", + "separator" + ] + }, + "LLMRAGQueryGeneratorConfig": { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "llm", + "default": "llm" + }, + "model": { + "type": "string" + }, + "template": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "type", + "model", + "template" + ] + }, + "RAGQueryConfig": { + "type": "object", + "properties": { + "query_generator_config": { + "$ref": "#/components/schemas/RAGQueryGeneratorConfig" + }, + "max_tokens_in_context": { + "type": "integer", + "default": 4096 + }, + "max_chunks": { + "type": "integer", + "default": 5 + } + }, + "additionalProperties": false, + "required": [ + "query_generator_config", + "max_tokens_in_context", + "max_chunks" + ] + }, + "RAGQueryGeneratorConfig": { + "oneOf": [ + { + "$ref": "#/components/schemas/DefaultRAGQueryGeneratorConfig" + }, + { + "$ref": "#/components/schemas/LLMRAGQueryGeneratorConfig" + } + ] + }, + "QueryRequest": { + "type": "object", + "properties": { + "content": { + "$ref": "#/components/schemas/InterleavedContent" + }, + "vector_db_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "query_config": { + "$ref": "#/components/schemas/RAGQueryConfig" + } + }, + "additionalProperties": false, + "required": [ + "content", + "vector_db_ids" + ] + }, + "RAGQueryResult": { + "type": "object", + "properties": { + "content": { + "$ref": "#/components/schemas/InterleavedContent" + } + }, + "additionalProperties": false + }, "QueryChunksRequest": { "type": "object", "properties": { @@ -7981,111 +8082,6 @@ "scores" ] }, - "DefaultRAGQueryGeneratorConfig": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "default", - "default": "default" - }, - "separator": { - "type": "string", - "default": " " - } - }, - "additionalProperties": false, - "required": [ - "type", - "separator" - ] - }, - "LLMRAGQueryGeneratorConfig": { - "type": "object", - "properties": { - "type": { - "type": "string", - "const": "llm", - "default": "llm" - }, - "model": { - "type": "string" - }, - "template": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "type", - "model", - "template" - ] - }, - "RAGQueryConfig": { - "type": "object", - "properties": { - "query_generator_config": { - "$ref": "#/components/schemas/RAGQueryGeneratorConfig" - }, - "max_tokens_in_context": { - "type": "integer", - "default": 4096 - }, - "max_chunks": { - "type": "integer", - "default": 5 - } - }, - "additionalProperties": false, - "required": [ - "query_generator_config", - "max_tokens_in_context", - "max_chunks" - ] - }, - "RAGQueryGeneratorConfig": { - "oneOf": [ - { - "$ref": "#/components/schemas/DefaultRAGQueryGeneratorConfig" - }, - { - "$ref": "#/components/schemas/LLMRAGQueryGeneratorConfig" - } - ] - }, - "QueryContextRequest": { - "type": "object", - "properties": { - "content": { - "$ref": "#/components/schemas/InterleavedContent" - }, - "query_config": { - "$ref": "#/components/schemas/RAGQueryConfig" - }, - "vector_db_ids": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "additionalProperties": false, - "required": [ - "content", - "query_config", - "vector_db_ids" - ] - }, - "RAGQueryResult": { - "type": "object", - "properties": { - "content": { - "$ref": "#/components/schemas/InterleavedContent" - } - }, - "additionalProperties": false - }, "QueryCondition": { "type": "object", "properties": { @@ -9246,8 +9242,8 @@ "description": "" }, { - "name": "InsertDocumentsRequest", - "description": "" + "name": "InsertRequest", + "description": "" }, { "name": "Inspect" @@ -9435,8 +9431,8 @@ "description": "" }, { - "name": "QueryContextRequest", - "description": "" + "name": "QueryRequest", + "description": "" }, { "name": "QuerySpanTreeResponse", @@ -9858,7 +9854,7 @@ "ImageDelta", "InferenceStep", "InsertChunksRequest", - "InsertDocumentsRequest", + "InsertRequest", "InterleavedContent", "InterleavedContentItem", "InvokeToolRequest", @@ -9903,7 +9899,7 @@ "QueryChunksResponse", "QueryCondition", "QueryConditionOp", - "QueryContextRequest", + "QueryRequest", "QuerySpanTreeResponse", "QuerySpansResponse", "QueryTracesResponse", diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index e1ae07c45..6bbaadf8d 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -1009,7 +1009,7 @@ components: - vector_db_id - chunks type: object - InsertDocumentsRequest: + InsertRequest: additionalProperties: false properties: chunk_size_in_tokens: @@ -1299,10 +1299,6 @@ components: type: string inserted_context: $ref: '#/components/schemas/InterleavedContent' - memory_bank_ids: - items: - type: string - type: array started_at: format: date-time type: string @@ -1314,11 +1310,13 @@ components: type: string turn_id: type: string + vector_db_ids: + type: string required: - turn_id - step_id - step_type - - memory_bank_ids + - vector_db_ids - inserted_context type: object Message: @@ -1710,7 +1708,7 @@ components: - gt - lt type: string - QueryContextRequest: + QueryRequest: additionalProperties: false properties: content: @@ -1723,7 +1721,6 @@ components: type: array required: - content - - query_config - vector_db_ids type: object QuerySpanTreeResponse: @@ -5176,7 +5173,7 @@ paths: description: OK tags: - ToolRuntime - /v1/tool-runtime/rag-tool/insert-documents: + /v1/tool-runtime/rag-tool/insert: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5197,7 +5194,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/InsertDocumentsRequest' + $ref: '#/components/schemas/InsertRequest' required: true responses: '200': @@ -5205,7 +5202,7 @@ paths: summary: Index documents so they can be used by the RAG system tags: - ToolRuntime - /v1/tool-runtime/rag-tool/query-context: + /v1/tool-runtime/rag-tool/query: post: parameters: - description: JSON-encoded provider data which will be made available to the @@ -5226,7 +5223,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/QueryContextRequest' + $ref: '#/components/schemas/QueryRequest' required: true responses: '200': @@ -5814,9 +5811,8 @@ tags: - description: name: InsertChunksRequest -- description: - name: InsertDocumentsRequest +- description: + name: InsertRequest - name: Inspect - description: @@ -5943,9 +5939,8 @@ tags: - description: name: QueryConditionOp -- description: - name: QueryContextRequest +- description: + name: QueryRequest - description: name: QuerySpanTreeResponse @@ -6245,7 +6240,7 @@ x-tagGroups: - ImageDelta - InferenceStep - InsertChunksRequest - - InsertDocumentsRequest + - InsertRequest - InterleavedContent - InterleavedContentItem - InvokeToolRequest @@ -6290,7 +6285,7 @@ x-tagGroups: - QueryChunksResponse - QueryCondition - QueryConditionOp - - QueryContextRequest + - QueryRequest - QuerySpanTreeResponse - QuerySpansResponse - QueryTracesResponse diff --git a/llama_stack/apis/tools/rag_tool.py b/llama_stack/apis/tools/rag_tool.py index 0247bb384..950367304 100644 --- a/llama_stack/apis/tools/rag_tool.py +++ b/llama_stack/apis/tools/rag_tool.py @@ -74,8 +74,8 @@ class RAGQueryConfig(BaseModel): @runtime_checkable @trace_protocol class RAGToolRuntime(Protocol): - @webmethod(route="/tool-runtime/rag-tool/insert-documents", method="POST") - async def insert_documents( + @webmethod(route="/tool-runtime/rag-tool/insert", method="POST") + async def insert( self, documents: List[RAGDocument], vector_db_id: str, @@ -84,12 +84,12 @@ class RAGToolRuntime(Protocol): """Index documents so they can be used by the RAG system""" ... - @webmethod(route="/tool-runtime/rag-tool/query-context", method="POST") - async def query_context( + @webmethod(route="/tool-runtime/rag-tool/query", method="POST") + async def query( self, content: InterleavedContent, - query_config: RAGQueryConfig, vector_db_ids: List[str], + query_config: Optional[RAGQueryConfig] = None, ) -> RAGQueryResult: """Query the RAG system for context; typically invoked by the agent""" ... diff --git a/llama_stack/apis/vector_io/vector_io.py b/llama_stack/apis/vector_io/vector_io.py index 5371b8918..8feeaa6d4 100644 --- a/llama_stack/apis/vector_io/vector_io.py +++ b/llama_stack/apis/vector_io/vector_io.py @@ -38,7 +38,7 @@ class VectorDBStore(Protocol): class VectorIO(Protocol): vector_db_store: VectorDBStore - # this will just block now until documents are inserted, but it should + # this will just block now until chunks are inserted, but it should # probably return a Job instance which can be polled for completion @webmethod(route="/vector-io/insert", method="POST") async def insert_chunks( diff --git a/llama_stack/distribution/routers/routers.py b/llama_stack/distribution/routers/routers.py index 3ae9833dc..6bb2045bd 100644 --- a/llama_stack/distribution/routers/routers.py +++ b/llama_stack/distribution/routers/routers.py @@ -414,25 +414,25 @@ class ToolRuntimeRouter(ToolRuntime): ) -> None: self.routing_table = routing_table - async def query_context( + async def query( self, content: InterleavedContent, - query_config: RAGQueryConfig, vector_db_ids: List[str], + query_config: Optional[RAGQueryConfig] = None, ) -> RAGQueryResult: return await self.routing_table.get_provider_impl( - "rag_tool.query_context" - ).query_context(content, query_config, vector_db_ids) + "query_from_memory" + ).query(content, vector_db_ids, query_config) - async def insert_documents( + async def insert( self, documents: List[RAGDocument], vector_db_id: str, chunk_size_in_tokens: int = 512, ) -> None: return await self.routing_table.get_provider_impl( - "rag_tool.insert_documents" - ).insert_documents(documents, vector_db_id, chunk_size_in_tokens) + "insert_into_memory" + ).insert(documents, vector_db_id, chunk_size_in_tokens) def __init__( self, @@ -441,10 +441,9 @@ class ToolRuntimeRouter(ToolRuntime): self.routing_table = routing_table # HACK ALERT this should be in sync with "get_all_api_endpoints()" - # TODO: make sure rag_tool vs builtin::memory is correct everywhere self.rag_tool = self.RagToolImpl(routing_table) - setattr(self, "rag_tool.query_context", self.rag_tool.query_context) - setattr(self, "rag_tool.insert_documents", self.rag_tool.insert_documents) + for method in ("query", "insert"): + setattr(self, f"rag_tool.{method}", getattr(self.rag_tool, method)) async def initialize(self) -> None: pass diff --git a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py index 2d0ad137b..75fd75afc 100644 --- a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py +++ b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py @@ -84,7 +84,7 @@ def make_random_string(length: int = 8): TOOLS_ATTACHMENT_KEY_REGEX = re.compile(r"__tools_attachment__=(\{.*?\})") -MEMORY_QUERY_TOOL = "rag_tool.query_context" +MEMORY_QUERY_TOOL = "query_from_memory" WEB_SEARCH_TOOL = "web_search" MEMORY_GROUP = "builtin::memory" @@ -432,16 +432,16 @@ class ChatAgent(ShieldRunnerMixin): ) ) ) - result = await self.tool_runtime_api.rag_tool.query_context( + result = await self.tool_runtime_api.rag_tool.query( content=concat_interleaved_content( [msg.content for msg in input_messages] ), + vector_db_ids=vector_db_ids, query_config=RAGQueryConfig( query_generator_config=DefaultRAGQueryGeneratorConfig(), max_tokens_in_context=4096, max_chunks=5, ), - vector_db_ids=vector_db_ids, ) retrieved_context = result.content @@ -882,7 +882,7 @@ class ChatAgent(ShieldRunnerMixin): ) for a in data ] - await self.tool_runtime_api.rag_tool.insert_documents( + await self.tool_runtime_api.rag_tool.insert( documents=documents, vector_db_id=vector_db_id, chunk_size_in_tokens=512, diff --git a/llama_stack/providers/inline/tool_runtime/memory/memory.py b/llama_stack/providers/inline/tool_runtime/memory/memory.py index d3f8b07dc..7798ed711 100644 --- a/llama_stack/providers/inline/tool_runtime/memory/memory.py +++ b/llama_stack/providers/inline/tool_runtime/memory/memory.py @@ -61,7 +61,7 @@ class MemoryToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, RAGToolRuntime): async def shutdown(self): pass - async def insert_documents( + async def insert( self, documents: List[RAGDocument], vector_db_id: str, @@ -87,15 +87,16 @@ class MemoryToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, RAGToolRuntime): vector_db_id=vector_db_id, ) - async def query_context( + async def query( self, content: InterleavedContent, - query_config: RAGQueryConfig, vector_db_ids: List[str], + query_config: Optional[RAGQueryConfig] = None, ) -> RAGQueryResult: if not vector_db_ids: return RAGQueryResult(content=None) + query_config = query_config or RAGQueryConfig() query = await generate_rag_query( query_config.query_generator_config, content, @@ -159,11 +160,11 @@ class MemoryToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, RAGToolRuntime): # encountering fatals. return [ ToolDef( - name="rag_tool.query_context", + name="query_from_memory", description="Retrieve context from memory", ), ToolDef( - name="rag_tool.insert_documents", + name="insert_into_memory", description="Insert documents into memory", ), ] diff --git a/llama_stack/providers/tests/tools/test_tools.py b/llama_stack/providers/tests/tools/test_tools.py index 62b18ea66..bb4265f94 100644 --- a/llama_stack/providers/tests/tools/test_tools.py +++ b/llama_stack/providers/tests/tools/test_tools.py @@ -96,14 +96,14 @@ class TestTools: ) # Insert documents into memory - await tools_impl.rag_tool.insert_documents( + await tools_impl.rag_tool.insert( documents=sample_documents, vector_db_id="test_bank", chunk_size_in_tokens=512, ) # Execute the memory tool - response = await tools_impl.rag_tool.query_context( + response = await tools_impl.rag_tool.query( content="What are the main topics covered in the documentation?", vector_db_ids=["test_bank"], ) diff --git a/llama_stack/providers/tests/vector_io/test_vector_store.py b/llama_stack/providers/tests/vector_io/test_vector_store.py index ef6bfca73..2a41a8982 100644 --- a/llama_stack/providers/tests/vector_io/test_vector_store.py +++ b/llama_stack/providers/tests/vector_io/test_vector_store.py @@ -11,11 +11,9 @@ from pathlib import Path import pytest -from llama_stack.providers.utils.memory.vector_store import ( - content_from_doc, - MemoryBankDocument, - URL, -) +from llama_stack.apis.tools import RAGDocument + +from llama_stack.providers.utils.memory.vector_store import content_from_doc, URL DUMMY_PDF_PATH = Path(os.path.abspath(__file__)).parent / "fixtures" / "dummy.pdf" @@ -41,33 +39,33 @@ class TestVectorStore: @pytest.mark.asyncio async def test_returns_content_from_pdf_data_uri(self): data_uri = data_url_from_file(DUMMY_PDF_PATH) - doc = MemoryBankDocument( + doc = RAGDocument( document_id="dummy", content=data_uri, mime_type="application/pdf", metadata={}, ) content = await content_from_doc(doc) - assert content == "Dummy PDF file" + assert content == "Dumm y PDF file" @pytest.mark.asyncio async def test_downloads_pdf_and_returns_content(self): # Using GitHub to host the PDF file url = "https://raw.githubusercontent.com/meta-llama/llama-stack/da035d69cfca915318eaf485770a467ca3c2a238/llama_stack/providers/tests/memory/fixtures/dummy.pdf" - doc = MemoryBankDocument( + doc = RAGDocument( document_id="dummy", content=url, mime_type="application/pdf", metadata={}, ) content = await content_from_doc(doc) - assert content == "Dummy PDF file" + assert content == "Dumm y PDF file" @pytest.mark.asyncio async def test_downloads_pdf_and_returns_content_with_url_object(self): # Using GitHub to host the PDF file url = "https://raw.githubusercontent.com/meta-llama/llama-stack/da035d69cfca915318eaf485770a467ca3c2a238/llama_stack/providers/tests/memory/fixtures/dummy.pdf" - doc = MemoryBankDocument( + doc = RAGDocument( document_id="dummy", content=URL( uri=url, @@ -76,4 +74,4 @@ class TestVectorStore: metadata={}, ) content = await content_from_doc(doc) - assert content == "Dummy PDF file" + assert content == "Dumm y PDF file" diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py index fe80100da..6fe0678b4 100644 --- a/tests/client-sdk/agents/test_agents.py +++ b/tests/client-sdk/agents/test_agents.py @@ -292,7 +292,7 @@ def test_rag_agent(llama_stack_client, agent_config): embedding_model="all-MiniLM-L6-v2", embedding_dimension=384, ) - llama_stack_client.tool_runtime.rag_tool.insert_documents( + llama_stack_client.tool_runtime.rag_tool.insert( documents=documents, vector_db_id=vector_db_id, chunk_size_in_tokens=512, @@ -321,4 +321,4 @@ def test_rag_agent(llama_stack_client, agent_config): ) logs = [str(log) for log in EventLogger().log(response) if log is not None] logs_str = "".join(logs) - assert "Tool:rag_tool.query_context" in logs_str + assert "Tool:query_from_memory" in logs_str diff --git a/tests/client-sdk/tool_runtime/test_rag_tool.py b/tests/client-sdk/tool_runtime/test_rag_tool.py index bce067268..baf5b6b40 100644 --- a/tests/client-sdk/tool_runtime/test_rag_tool.py +++ b/tests/client-sdk/tool_runtime/test_rag_tool.py @@ -73,7 +73,7 @@ def test_vector_db_insert_inline_and_query( llama_stack_client, single_entry_vector_db_registry, sample_documents ): vector_db_id = single_entry_vector_db_registry[0] - llama_stack_client.tool_runtime.rag_tool.insert_documents( + llama_stack_client.tool_runtime.rag_tool.insert( documents=sample_documents, chunk_size_in_tokens=512, vector_db_id=vector_db_id, @@ -157,7 +157,7 @@ def test_vector_db_insert_from_url_and_query( for i, url in enumerate(urls) ] - llama_stack_client.tool_runtime.rag_tool.insert_documents( + llama_stack_client.tool_runtime.rag_tool.insert( documents=documents, vector_db_id=vector_db_id, chunk_size_in_tokens=512, From caa8387dd26546d2703b3291d339e8374934b966 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Wed, 22 Jan 2025 11:25:10 -0800 Subject: [PATCH 075/107] Fix fireworks client sdk chat completion with images (#840) Enable downloads before sending request to fireworks. Test using -- `LLAMA_STACK_CONFIG=./llama_stack/templates/fireworks/run.yaml pytest -s -v -k 'test_image_chat_completion_streaming' tests/client-sdk` --- llama_stack/providers/remote/inference/fireworks/fireworks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llama_stack/providers/remote/inference/fireworks/fireworks.py b/llama_stack/providers/remote/inference/fireworks/fireworks.py index e22144326..5c98d2054 100644 --- a/llama_stack/providers/remote/inference/fireworks/fireworks.py +++ b/llama_stack/providers/remote/inference/fireworks/fireworks.py @@ -265,7 +265,8 @@ class FireworksInferenceAdapter( if isinstance(request, ChatCompletionRequest): if media_present: input_dict["messages"] = [ - await convert_message_to_openai_dict(m) for m in request.messages + await convert_message_to_openai_dict(m, download=True) + for m in request.messages ] else: input_dict["prompt"] = await chat_completion_request_to_prompt( From 07b87365abd23dc21f983b1e97ca0952975d5a15 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 12:16:18 -0800 Subject: [PATCH 076/107] [inference api] modify content types so they follow a more standard structure (#841) Some small updates to the inference types to make them more standard Specifically: - image data is now located in a "image" subkey - similarly tool call data is located in a "tool_call" subkey The pattern followed is `dict(type="foo", foo=<...>)` --- docs/resources/llama-stack-spec.html | 31 +++++++++------- docs/resources/llama-stack-spec.yaml | 27 ++++++++------ llama_stack/apis/agents/event_logger.py | 2 +- llama_stack/apis/common/content_types.py | 7 ++-- .../agents/meta_reference/agent_instance.py | 6 ++-- .../inference/meta_reference/inference.py | 8 ++--- .../remote/inference/groq/groq_utils.py | 2 +- .../remote/inference/nvidia/openai_utils.py | 4 ++- .../tests/inference/groq/test_groq_utils.py | 2 +- .../tests/inference/test_text_inference.py | 6 ++-- .../tests/inference/test_vision_inference.py | 14 +++++--- .../utils/inference/openai_compat.py | 8 ++--- .../utils/inference/prompt_adapter.py | 36 ++++++++++--------- tests/client-sdk/inference/test_inference.py | 25 +++++++------ tests/client-sdk/safety/test_safety.py | 2 +- 15 files changed, 104 insertions(+), 76 deletions(-) diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index f6dd1c8dc..139314776 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -3761,22 +3761,29 @@ "ImageContentItem": { "type": "object", "properties": { - "url": { - "$ref": "#/components/schemas/URL" - }, - "data": { - "type": "string", - "contentEncoding": "base64" - }, "type": { "type": "string", "const": "image", "default": "image" + }, + "image": { + "type": "object", + "properties": { + "url": { + "$ref": "#/components/schemas/URL" + }, + "data": { + "type": "string", + "contentEncoding": "base64" + } + }, + "additionalProperties": false } }, "additionalProperties": false, "required": [ - "type" + "type", + "image" ] }, "InterleavedContent": { @@ -4518,7 +4525,7 @@ "const": "image", "default": "image" }, - "data": { + "image": { "type": "string", "contentEncoding": "base64" } @@ -4526,7 +4533,7 @@ "additionalProperties": false, "required": [ "type", - "data" + "image" ] }, "TextDelta": { @@ -4570,7 +4577,7 @@ "const": "tool_call", "default": "tool_call" }, - "content": { + "tool_call": { "oneOf": [ { "type": "string" @@ -4587,7 +4594,7 @@ "additionalProperties": false, "required": [ "type", - "content", + "tool_call", "parse_status" ] }, diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index 6bbaadf8d..1a8c44bc0 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -926,22 +926,27 @@ components: ImageContentItem: additionalProperties: false properties: - data: - contentEncoding: base64 - type: string + image: + additionalProperties: false + properties: + data: + contentEncoding: base64 + type: string + url: + $ref: '#/components/schemas/URL' + type: object type: const: image default: image type: string - url: - $ref: '#/components/schemas/URL' required: - type + - image type: object ImageDelta: additionalProperties: false properties: - data: + image: contentEncoding: base64 type: string type: @@ -950,7 +955,7 @@ components: type: string required: - type - - data + - image type: object InferenceStep: additionalProperties: false @@ -2748,19 +2753,19 @@ components: ToolCallDelta: additionalProperties: false properties: - content: + parse_status: + $ref: '#/components/schemas/ToolCallParseStatus' + tool_call: oneOf: - type: string - $ref: '#/components/schemas/ToolCall' - parse_status: - $ref: '#/components/schemas/ToolCallParseStatus' type: const: tool_call default: tool_call type: string required: - type - - content + - tool_call - parse_status type: object ToolCallParseStatus: diff --git a/llama_stack/apis/agents/event_logger.py b/llama_stack/apis/agents/event_logger.py index ddb2a7cf4..7a607ffda 100644 --- a/llama_stack/apis/agents/event_logger.py +++ b/llama_stack/apis/agents/event_logger.py @@ -137,7 +137,7 @@ class EventLogger: event, LogEvent( role=None, - content=delta.content, + content=delta.tool_call, end="", color="cyan", ), diff --git a/llama_stack/apis/common/content_types.py b/llama_stack/apis/common/content_types.py index b845d09dd..1d8cea567 100644 --- a/llama_stack/apis/common/content_types.py +++ b/llama_stack/apis/common/content_types.py @@ -38,8 +38,9 @@ class _URLOrData(BaseModel): @json_schema_type -class ImageContentItem(_URLOrData): +class ImageContentItem(BaseModel): type: Literal["image"] = "image" + image: _URLOrData @json_schema_type @@ -73,7 +74,7 @@ class TextDelta(BaseModel): @json_schema_type class ImageDelta(BaseModel): type: Literal["image"] = "image" - data: bytes + image: bytes @json_schema_type @@ -91,7 +92,7 @@ class ToolCallDelta(BaseModel): # you either send an in-progress tool call so the client can stream a long # code generation or you send the final parsed tool call at the end of the # stream - content: Union[str, ToolCall] + tool_call: Union[str, ToolCall] parse_status: ToolCallParseStatus diff --git a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py index 75fd75afc..1b375fba7 100644 --- a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py +++ b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py @@ -423,7 +423,7 @@ class ChatAgent(ShieldRunnerMixin): step_id=step_id, delta=ToolCallDelta( parse_status=ToolCallParseStatus.succeeded, - content=ToolCall( + tool_call=ToolCall( call_id="", tool_name=MEMORY_QUERY_TOOL, arguments={}, @@ -525,7 +525,7 @@ class ChatAgent(ShieldRunnerMixin): delta = event.delta if delta.type == "tool_call": if delta.parse_status == ToolCallParseStatus.succeeded: - tool_calls.append(delta.content) + tool_calls.append(delta.tool_call) if stream: yield AgentTurnResponseStreamChunk( event=AgentTurnResponseEvent( @@ -639,7 +639,7 @@ class ChatAgent(ShieldRunnerMixin): tool_call=tool_call, delta=ToolCallDelta( parse_status=ToolCallParseStatus.in_progress, - content=tool_call, + tool_call=tool_call, ), ) ) diff --git a/llama_stack/providers/inline/inference/meta_reference/inference.py b/llama_stack/providers/inline/inference/meta_reference/inference.py index 31ad6fa28..73962ca7f 100644 --- a/llama_stack/providers/inline/inference/meta_reference/inference.py +++ b/llama_stack/providers/inline/inference/meta_reference/inference.py @@ -377,7 +377,7 @@ class MetaReferenceInferenceImpl( event=ChatCompletionResponseEvent( event_type=ChatCompletionResponseEventType.progress, delta=ToolCallDelta( - content="", + tool_call="", parse_status=ToolCallParseStatus.started, ), ) @@ -395,7 +395,7 @@ class MetaReferenceInferenceImpl( if ipython: delta = ToolCallDelta( - content=text, + tool_call=text, parse_status=ToolCallParseStatus.in_progress, ) else: @@ -434,7 +434,7 @@ class MetaReferenceInferenceImpl( event=ChatCompletionResponseEvent( event_type=ChatCompletionResponseEventType.progress, delta=ToolCallDelta( - content="", + tool_call="", parse_status=ToolCallParseStatus.failed, ), stop_reason=stop_reason, @@ -446,7 +446,7 @@ class MetaReferenceInferenceImpl( event=ChatCompletionResponseEvent( event_type=ChatCompletionResponseEventType.progress, delta=ToolCallDelta( - content=tool_call, + tool_call=tool_call, parse_status=ToolCallParseStatus.succeeded, ), stop_reason=stop_reason, diff --git a/llama_stack/providers/remote/inference/groq/groq_utils.py b/llama_stack/providers/remote/inference/groq/groq_utils.py index b614c90f4..bd1a07d7c 100644 --- a/llama_stack/providers/remote/inference/groq/groq_utils.py +++ b/llama_stack/providers/remote/inference/groq/groq_utils.py @@ -218,7 +218,7 @@ async def convert_chat_completion_response_stream( event=ChatCompletionResponseEvent( event_type=event_type, delta=ToolCallDelta( - content=tool_call, + tool_call=tool_call, parse_status=ToolCallParseStatus.succeeded, ), ) diff --git a/llama_stack/providers/remote/inference/nvidia/openai_utils.py b/llama_stack/providers/remote/inference/nvidia/openai_utils.py index e85c8dd21..0f753f80d 100644 --- a/llama_stack/providers/remote/inference/nvidia/openai_utils.py +++ b/llama_stack/providers/remote/inference/nvidia/openai_utils.py @@ -505,7 +505,9 @@ async def convert_openai_chat_completion_stream( event=ChatCompletionResponseEvent( event_type=next(event_type), delta=ToolCallDelta( - content=_convert_openai_tool_calls(choice.delta.tool_calls)[0], + tool_call=_convert_openai_tool_calls(choice.delta.tool_calls)[ + 0 + ], parse_status=ToolCallParseStatus.succeeded, ), logprobs=_convert_openai_logprobs(choice.logprobs), diff --git a/llama_stack/providers/tests/inference/groq/test_groq_utils.py b/llama_stack/providers/tests/inference/groq/test_groq_utils.py index 0402a772c..f6f593f16 100644 --- a/llama_stack/providers/tests/inference/groq/test_groq_utils.py +++ b/llama_stack/providers/tests/inference/groq/test_groq_utils.py @@ -472,7 +472,7 @@ class TestConvertStreamChatCompletionResponse: iter = converted.__aiter__() chunk = await iter.__anext__() assert chunk.event.event_type == ChatCompletionResponseEventType.start - assert chunk.event.delta.content == ToolCall( + assert chunk.event.delta.tool_call == ToolCall( call_id="tool_call_id", tool_name="get_flight_info", arguments={"origin": "AU", "destination": "LAX"}, diff --git a/llama_stack/providers/tests/inference/test_text_inference.py b/llama_stack/providers/tests/inference/test_text_inference.py index cbc8232c8..c39556b8e 100644 --- a/llama_stack/providers/tests/inference/test_text_inference.py +++ b/llama_stack/providers/tests/inference/test_text_inference.py @@ -470,16 +470,16 @@ class TestInference: ) first = grouped[ChatCompletionResponseEventType.progress][0] if not isinstance( - first.event.delta.content, ToolCall + first.event.delta.tool_call, ToolCall ): # first chunk may contain entire call assert first.event.delta.parse_status == ToolCallParseStatus.started last = grouped[ChatCompletionResponseEventType.progress][-1] # assert last.event.stop_reason == expected_stop_reason assert last.event.delta.parse_status == ToolCallParseStatus.succeeded - assert isinstance(last.event.delta.content, ToolCall) + assert isinstance(last.event.delta.tool_call, ToolCall) - call = last.event.delta.content + call = last.event.delta.tool_call assert call.tool_name == "get_weather" assert "location" in call.arguments assert "San Francisco" in call.arguments["location"] diff --git a/llama_stack/providers/tests/inference/test_vision_inference.py b/llama_stack/providers/tests/inference/test_vision_inference.py index df2f3cfb9..100a70236 100644 --- a/llama_stack/providers/tests/inference/test_vision_inference.py +++ b/llama_stack/providers/tests/inference/test_vision_inference.py @@ -32,13 +32,15 @@ class TestVisionModelInference: "image, expected_strings", [ ( - ImageContentItem(data=PASTA_IMAGE), + ImageContentItem(image=dict(data=PASTA_IMAGE)), ["spaghetti"], ), ( ImageContentItem( - url=URL( - uri="https://www.healthypawspetinsurance.com/Images/V3/DogAndPuppyInsurance/Dog_CTA_Desktop_HeroImage.jpg" + image=dict( + url=URL( + uri="https://www.healthypawspetinsurance.com/Images/V3/DogAndPuppyInsurance/Dog_CTA_Desktop_HeroImage.jpg" + ) ) ), ["puppy"], @@ -103,8 +105,10 @@ class TestVisionModelInference: images = [ ImageContentItem( - url=URL( - uri="https://www.healthypawspetinsurance.com/Images/V3/DogAndPuppyInsurance/Dog_CTA_Desktop_HeroImage.jpg" + image=dict( + url=URL( + uri="https://www.healthypawspetinsurance.com/Images/V3/DogAndPuppyInsurance/Dog_CTA_Desktop_HeroImage.jpg" + ) ) ), ] diff --git a/llama_stack/providers/utils/inference/openai_compat.py b/llama_stack/providers/utils/inference/openai_compat.py index 127fd19f3..6c93f49c0 100644 --- a/llama_stack/providers/utils/inference/openai_compat.py +++ b/llama_stack/providers/utils/inference/openai_compat.py @@ -240,7 +240,7 @@ async def process_chat_completion_stream_response( event=ChatCompletionResponseEvent( event_type=ChatCompletionResponseEventType.progress, delta=ToolCallDelta( - content="", + tool_call="", parse_status=ToolCallParseStatus.started, ), ) @@ -260,7 +260,7 @@ async def process_chat_completion_stream_response( if ipython: buffer += text delta = ToolCallDelta( - content=text, + tool_call=text, parse_status=ToolCallParseStatus.in_progress, ) @@ -289,7 +289,7 @@ async def process_chat_completion_stream_response( event=ChatCompletionResponseEvent( event_type=ChatCompletionResponseEventType.progress, delta=ToolCallDelta( - content="", + tool_call="", parse_status=ToolCallParseStatus.failed, ), stop_reason=stop_reason, @@ -301,7 +301,7 @@ async def process_chat_completion_stream_response( event=ChatCompletionResponseEvent( event_type=ChatCompletionResponseEventType.progress, delta=ToolCallDelta( - content=tool_call, + tool_call=tool_call, parse_status=ToolCallParseStatus.succeeded, ), stop_reason=stop_reason, diff --git a/llama_stack/providers/utils/inference/prompt_adapter.py b/llama_stack/providers/utils/inference/prompt_adapter.py index 701b2ca3b..f5298d844 100644 --- a/llama_stack/providers/utils/inference/prompt_adapter.py +++ b/llama_stack/providers/utils/inference/prompt_adapter.py @@ -113,28 +113,29 @@ async def interleaved_content_convert_to_raw( elif isinstance(c, TextContentItem): return RawTextItem(text=c.text) elif isinstance(c, ImageContentItem): - if c.url: + image = c.image + if image.url: # Load image bytes from URL - if c.url.uri.startswith("data"): - match = re.match(r"data:image/(\w+);base64,(.+)", c.url.uri) + if image.url.uri.startswith("data"): + match = re.match(r"data:image/(\w+);base64,(.+)", image.url.uri) if not match: raise ValueError( - f"Invalid data URL format, {c.url.uri[:40]}..." + f"Invalid data URL format, {image.url.uri[:40]}..." ) _, image_data = match.groups() data = base64.b64decode(image_data) - elif c.url.uri.startswith("file://"): - path = c.url.uri[len("file://") :] + elif image.url.uri.startswith("file://"): + path = image.url.uri[len("file://") :] with open(path, "rb") as f: data = f.read() # type: ignore - elif c.url.uri.startswith("http"): + elif image.url.uri.startswith("http"): async with httpx.AsyncClient() as client: - response = await client.get(c.url.uri) + response = await client.get(image.url.uri) data = response.content else: raise ValueError("Unsupported URL type") - elif c.data: - data = c.data + elif image.data: + data = image.data else: raise ValueError("No data or URL provided") @@ -170,26 +171,29 @@ def request_has_media(request: Union[ChatCompletionRequest, CompletionRequest]): async def localize_image_content(media: ImageContentItem) -> Tuple[bytes, str]: - if media.url and media.url.uri.startswith("http"): + image = media.image + if image.url and image.url.uri.startswith("http"): async with httpx.AsyncClient() as client: - r = await client.get(media.url.uri) + r = await client.get(image.url.uri) content = r.content content_type = r.headers.get("content-type") if content_type: format = content_type.split("/")[-1] else: format = "png" + return content, format else: - image = PIL_Image.open(io.BytesIO(media.data)) - return media.data, image.format + pil_image = PIL_Image.open(io.BytesIO(image.data)) + return image.data, pil_image.format async def convert_image_content_to_url( media: ImageContentItem, download: bool = False, include_format: bool = True ) -> str: - if media.url and (not download or media.url.uri.startswith("data")): - return media.url.uri + image = media.image + if image.url and (not download or image.url.uri.startswith("data")): + return image.url.uri content, format = await localize_image_content(media) if include_format: diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py index 08c7e1693..b1f1dd139 100644 --- a/tests/client-sdk/inference/test_inference.py +++ b/tests/client-sdk/inference/test_inference.py @@ -258,7 +258,7 @@ def extract_tool_invocation_content(response): for chunk in response: delta = chunk.event.delta if delta.type == "tool_call" and delta.parse_status == "succeeded": - call = delta.content + call = delta.tool_call tool_invocation_content += f"[{call.tool_name}, {call.arguments}]" return tool_invocation_content @@ -321,9 +321,11 @@ def test_image_chat_completion_non_streaming(llama_stack_client, vision_model_id "content": [ { "type": "image", - "url": { - # TODO: Replace with Github based URI to resources/sample1.jpg - "uri": "https://www.healthypawspetinsurance.com/Images/V3/DogAndPuppyInsurance/Dog_CTA_Desktop_HeroImage.jpg" + "image": { + "url": { + # TODO: Replace with Github based URI to resources/sample1.jpg + "uri": "https://www.healthypawspetinsurance.com/Images/V3/DogAndPuppyInsurance/Dog_CTA_Desktop_HeroImage.jpg" + }, }, }, { @@ -348,9 +350,11 @@ def test_image_chat_completion_streaming(llama_stack_client, vision_model_id): "content": [ { "type": "image", - "url": { - # TODO: Replace with Github based URI to resources/sample1.jpg - "uri": "https://www.healthypawspetinsurance.com/Images/V3/DogAndPuppyInsurance/Dog_CTA_Desktop_HeroImage.jpg" + "image": { + "url": { + # TODO: Replace with Github based URI to resources/sample1.jpg + "uri": "https://www.healthypawspetinsurance.com/Images/V3/DogAndPuppyInsurance/Dog_CTA_Desktop_HeroImage.jpg" + }, }, }, { @@ -374,14 +378,15 @@ def test_image_chat_completion_streaming(llama_stack_client, vision_model_id): def test_image_chat_completion_base64_url( llama_stack_client, vision_model_id, base64_image_url ): - message = { "role": "user", "content": [ { "type": "image", - "url": { - "uri": base64_image_url, + "image": { + "url": { + "uri": base64_image_url, + }, }, }, { diff --git a/tests/client-sdk/safety/test_safety.py b/tests/client-sdk/safety/test_safety.py index 6af417a09..ac3221364 100644 --- a/tests/client-sdk/safety/test_safety.py +++ b/tests/client-sdk/safety/test_safety.py @@ -141,7 +141,7 @@ def test_safety_with_image(llama_stack_client, model_providers): }, { "type": "image", - "url": {"uri": data_url_from_image(file_path)}, + "image": {"url": {"uri": data_url_from_image(file_path)}}, }, ], } From 55d01339c2aa9e425aaa6433f46b04db0d961ece Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 13:31:11 -0800 Subject: [PATCH 077/107] Update notebook --- ...Llama_Stack_Building_AI_Applications.ipynb | 8955 ++++++++--------- 1 file changed, 4377 insertions(+), 4578 deletions(-) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index 5857901bd..4c3f680fd 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -83,8 +83,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Reading package lists... Done\n", "Building dependency tree... Done\n", @@ -230,8 +230,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Requirement already satisfied: llama-stack in /usr/local/lib/python3.11/dist-packages (0.1.0rc10)\r\n", "Requirement already satisfied: blobfile in /usr/local/lib/python3.11/dist-packages (from llama-stack) (3.0.0)\r\n", @@ -571,7 +571,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "E1UFuJC570Tk", "metadata": { "colab": { @@ -707,395 +707,53 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", - "text": [ - "Removed handler StreamHandler from root logger\n" - ] - }, - { "output_type": "stream", - "name": "stderr", "text": [ - "/usr/local/lib/python3.11/dist-packages/huggingface_hub/utils/_auth.py:94: UserWarning: \n", - "The secret `HF_TOKEN` does not exist in your Colab secrets.\n", - "To authenticate with the Hugging Face Hub, create a token in your settings tab (https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session.\n", - "You will be able to reuse this secret in all of your notebooks.\n", - "Please note that authentication is recommended but still optional to access public models or datasets.\n", - " warnings.warn(\n" + "Not in Google Colab environment\n", + "\u001b[33mWarning: `bwrap` is not available. Code interpreter tool will not work correctly.\u001b[0m\n" ] }, { - "output_type": "display_data", - "data": { - "text/plain": [ - "modules.json: 0%| | 0.00/349 [00:00Using config together:\n", "
\n" + ], + "text/plain": [ + "Using config \u001b[34mtogether\u001b[0m:\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" }, { - "output_type": "display_data", "data": { - "text/plain": [ - "apis:\n", - "- agents\n", - "- datasetio\n", - "- eval\n", - "- inference\n", - "- memory\n", - "- safety\n", - "- scoring\n", - "- telemetry\n", - "- tool_runtime\n", - "container_image: null\n", - "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "image_name: together\n", - "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "metadata_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - "models:\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-FP8\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Meta-Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - llm\n", - " provider_id: together\n", - " provider_model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision-Turbo\n", - "- metadata:\n", - " embedding_dimension: \u001b[1;36m384\u001b[0m\n", - " model_id: all-MiniLM-L6-v2\n", - " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", - " - embedding\n", - " provider_id: sentence-transformers\n", - " provider_model_id: null\n", - "providers:\n", - " agents:\n", - " - config:\n", - " persistence_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - " datasetio:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: huggingface\n", - " provider_type: remote::huggingface\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: localfs\n", - " provider_type: inline::localfs\n", - " eval:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - " inference:\n", - " - config:\n", - " api_key: \u001b[32m'********'\u001b[0m\n", - " url: \u001b[4;94mhttps://api.together.xyz/v1\u001b[0m\n", - " provider_id: together\n", - " provider_type: remote::together\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: sentence-transformers\n", - " provider_type: inline::sentence-transformers\n", - " memory:\n", - " - config:\n", - " kvstore:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - " provider_id: faiss\n", - " provider_type: inlin\u001b[1;92me::fa\u001b[0miss\n", - " safety:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: llama-guard\n", - " provider_type: inline::llama-guard\n", - " scoring:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: basic\n", - " provider_type: inlin\u001b[1;92me::ba\u001b[0msic\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: llm-as-judge\n", - " provider_type: inline::llm-as-judge\n", - " - config:\n", - " openai_api_key: \u001b[32m'********'\u001b[0m\n", - " provider_id: braintrust\n", - " provider_type: inlin\u001b[1;92me::b\u001b[0mraintrust\n", - " telemetry:\n", - " - config:\n", - " service_name: llama-stack\n", - " sinks: sqlite\n", - " sqlite_db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - " tool_runtime:\n", - " - config:\n", - " api_key: \u001b[32m'********'\u001b[0m\n", - " max_results: \u001b[1;36m3\u001b[0m\n", - " provider_id: brave-search\n", - " provider_type: remot\u001b[1;92me::b\u001b[0mrave-search\n", - " - config:\n", - " api_key: \u001b[32m'********'\u001b[0m\n", - " max_results: \u001b[1;36m3\u001b[0m\n", - " provider_id: tavily-search\n", - " provider_type: remote::tavily-search\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: code-interpreter\n", - " provider_type: inlin\u001b[1;92me::c\u001b[0mode-interpreter\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: memory-runtime\n", - " provider_type: inline::memory-runtime\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: model-context-protocol\n", - " provider_type: remote::model-context-protocol\n", - "scoring_fns: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "shields:\n", - "- params: null\n", - " provider_id: null\n", - " provider_shield_id: null\n", - " shield_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - "tool_groups:\n", - "- args: null\n", - " mcp_endpoint: null\n", - " provider_id: tavily-search\n", - " toolgroup_id: builtin::websearch\n", - "- args: null\n", - " mcp_endpoint: null\n", - " provider_id: memory-runtime\n", - " toolgroup_id: builtin::memory\n", - "- args: null\n", - " mcp_endpoint: null\n", - " provider_id: code-interpreter\n", - " toolgroup_id: builtin::code_interpreter\n", - "version: \u001b[32m'2'\u001b[0m\n", - "\n" - ], "text/html": [ "
apis:\n",
               "- agents\n",
               "- datasetio\n",
               "- eval\n",
               "- inference\n",
-              "- memory\n",
               "- safety\n",
               "- scoring\n",
               "- telemetry\n",
               "- tool_runtime\n",
+              "- vector_io\n",
               "container_image: null\n",
               "datasets: []\n",
               "eval_tasks: []\n",
               "image_name: together\n",
-              "memory_banks: []\n",
               "metadata_store:\n",
-              "  db_path: /root/.llama/distributions/together/registry.db\n",
+              "  db_path: /Users/ashwin/.llama/distributions/together/registry.db\n",
               "  namespace: null\n",
               "  type: sqlite\n",
               "models:\n",
@@ -1164,7 +822,7 @@
               "  agents:\n",
               "  - config:\n",
               "      persistence_store:\n",
-              "        db_path: /root/.llama/distributions/together/agents_store.db\n",
+              "        db_path: /Users/ashwin/.llama/distributions/together/agents_store.db\n",
               "        namespace: null\n",
               "        type: sqlite\n",
               "    provider_id: meta-reference\n",
@@ -1189,14 +847,6 @@
               "  - config: {}\n",
               "    provider_id: sentence-transformers\n",
               "    provider_type: inline::sentence-transformers\n",
-              "  memory:\n",
-              "  - config:\n",
-              "      kvstore:\n",
-              "        db_path: /root/.llama/distributions/together/faiss_store.db\n",
-              "        namespace: null\n",
-              "        type: sqlite\n",
-              "    provider_id: faiss\n",
-              "    provider_type: inline::faiss\n",
               "  safety:\n",
               "  - config: {}\n",
               "    provider_id: llama-guard\n",
@@ -1216,7 +866,7 @@
               "  - config:\n",
               "      service_name: llama-stack\n",
               "      sinks: sqlite\n",
-              "      sqlite_db_path: /root/.llama/distributions/together/trace_store.db\n",
+              "      sqlite_db_path: /Users/ashwin/.llama/distributions/together/trace_store.db\n",
               "    provider_id: meta-reference\n",
               "    provider_type: inline::meta-reference\n",
               "  tool_runtime:\n",
@@ -1239,6 +889,14 @@
               "  - config: {}\n",
               "    provider_id: model-context-protocol\n",
               "    provider_type: remote::model-context-protocol\n",
+              "  vector_io:\n",
+              "  - config:\n",
+              "      kvstore:\n",
+              "        db_path: /Users/ashwin/.llama/distributions/together/faiss_store.db\n",
+              "        namespace: null\n",
+              "        type: sqlite\n",
+              "    provider_id: faiss\n",
+              "    provider_type: inline::faiss\n",
               "scoring_fns: []\n",
               "shields:\n",
               "- params: null\n",
@@ -1258,16 +916,204 @@
               "  mcp_endpoint: null\n",
               "  provider_id: code-interpreter\n",
               "  toolgroup_id: builtin::code_interpreter\n",
+              "vector_dbs: []\n",
               "version: '2'\n",
               "\n",
               "
\n" + ], + "text/plain": [ + "apis:\n", + "- agents\n", + "- datasetio\n", + "- eval\n", + "- inference\n", + "- safety\n", + "- scoring\n", + "- telemetry\n", + "- tool_runtime\n", + "- vector_io\n", + "container_image: null\n", + "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "image_name: together\n", + "metadata_store:\n", + " db_path: \u001b[35m/Users/ashwin/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", + " namespace: null\n", + " type: sqlite\n", + "models:\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-FP8\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-\u001b[1;36m3.3\u001b[0m-70B-Instruct-Turbo\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Meta-Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", + "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - llm\n", + " provider_id: together\n", + " provider_model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision-Turbo\n", + "- metadata:\n", + " embedding_dimension: \u001b[1;36m384\u001b[0m\n", + " model_id: all-MiniLM-L6-v2\n", + " model_type: !!python/object/apply:llama_stack.apis.models.models.ModelType\n", + " - embedding\n", + " provider_id: sentence-transformers\n", + " provider_model_id: null\n", + "providers:\n", + " agents:\n", + " - config:\n", + " persistence_store:\n", + " db_path: \u001b[35m/Users/ashwin/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", + " namespace: null\n", + " type: sqlite\n", + " provider_id: meta-reference\n", + " provider_type: inline::meta-reference\n", + " datasetio:\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: huggingface\n", + " provider_type: remote::huggingface\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: localfs\n", + " provider_type: inline::localfs\n", + " eval:\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: meta-reference\n", + " provider_type: inline::meta-reference\n", + " inference:\n", + " - config:\n", + " api_key: \u001b[32m'********'\u001b[0m\n", + " url: \u001b[4;94mhttps://api.together.xyz/v1\u001b[0m\n", + " provider_id: together\n", + " provider_type: remote::together\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: sentence-transformers\n", + " provider_type: inline::sentence-transformers\n", + " safety:\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: llama-guard\n", + " provider_type: inline::llama-guard\n", + " scoring:\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: basic\n", + " provider_type: inlin\u001b[1;92me::ba\u001b[0msic\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: llm-as-judge\n", + " provider_type: inline::llm-as-judge\n", + " - config:\n", + " openai_api_key: \u001b[32m'********'\u001b[0m\n", + " provider_id: braintrust\n", + " provider_type: inlin\u001b[1;92me::b\u001b[0mraintrust\n", + " telemetry:\n", + " - config:\n", + " service_name: llama-stack\n", + " sinks: sqlite\n", + " sqlite_db_path: \u001b[35m/Users/ashwin/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", + " provider_id: meta-reference\n", + " provider_type: inline::meta-reference\n", + " tool_runtime:\n", + " - config:\n", + " api_key: \u001b[32m'********'\u001b[0m\n", + " max_results: \u001b[1;36m3\u001b[0m\n", + " provider_id: brave-search\n", + " provider_type: remot\u001b[1;92me::b\u001b[0mrave-search\n", + " - config:\n", + " api_key: \u001b[32m'********'\u001b[0m\n", + " max_results: \u001b[1;36m3\u001b[0m\n", + " provider_id: tavily-search\n", + " provider_type: remote::tavily-search\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: code-interpreter\n", + " provider_type: inlin\u001b[1;92me::c\u001b[0mode-interpreter\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: memory-runtime\n", + " provider_type: inline::memory-runtime\n", + " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", + " provider_id: model-context-protocol\n", + " provider_type: remote::model-context-protocol\n", + " vector_io:\n", + " - config:\n", + " kvstore:\n", + " db_path: \u001b[35m/Users/ashwin/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", + " namespace: null\n", + " type: sqlite\n", + " provider_id: faiss\n", + " provider_type: inlin\u001b[1;92me::fa\u001b[0miss\n", + "scoring_fns: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "shields:\n", + "- params: null\n", + " provider_id: null\n", + " provider_shield_id: null\n", + " shield_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", + "tool_groups:\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: tavily-search\n", + " toolgroup_id: builtin::websearch\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: memory-runtime\n", + " toolgroup_id: builtin::memory\n", + "- args: null\n", + " mcp_endpoint: null\n", + " provider_id: code-interpreter\n", + " toolgroup_id: builtin::code_interpreter\n", + "vector_dbs: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", + "version: \u001b[32m'2'\u001b[0m\n", + "\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } ], "source": [ "import os\n", + "\n", + "os.environ[\"TOGETHER_API_KEY\"] = \"2d8335559c046920fd3ccffae6d7057353b289d6272d5e979621457eb330e82b\"\n", + "os.environ[\"TAVILY_SEARCH_API_KEY\"] = \"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\"\n", "try:\n", " from google.colab import userdata\n", " os.environ['TOGETHER_API_KEY'] = userdata.get('TOGETHER_API_KEY')\n", @@ -1306,7 +1152,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "ruO9jQna_t_S", "metadata": { "colab": { @@ -1318,20 +1164,20 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Available models:\n", - "meta-llama/Llama-3.1-8B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo) \n", - "meta-llama/Llama-3.1-70B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo) \n", + "all-MiniLM-L6-v2 (provider's alias: all-MiniLM-L6-v2) \n", "meta-llama/Llama-3.1-405B-Instruct-FP8 (provider's alias: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo) \n", - "meta-llama/Llama-3.2-3B-Instruct (provider's alias: meta-llama/Llama-3.2-3B-Instruct-Turbo) \n", + "meta-llama/Llama-3.1-70B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo) \n", + "meta-llama/Llama-3.1-8B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo) \n", "meta-llama/Llama-3.2-11B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo) \n", + "meta-llama/Llama-3.2-3B-Instruct (provider's alias: meta-llama/Llama-3.2-3B-Instruct-Turbo) \n", "meta-llama/Llama-3.2-90B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo) \n", "meta-llama/Llama-3.3-70B-Instruct (provider's alias: meta-llama/Llama-3.3-70B-Instruct-Turbo) \n", - "meta-llama/Llama-Guard-3-8B (provider's alias: meta-llama/Meta-Llama-Guard-3-8B) \n", "meta-llama/Llama-Guard-3-11B-Vision (provider's alias: meta-llama/Llama-Guard-3-11B-Vision-Turbo) \n", - "all-MiniLM-L6-v2 (provider's alias: all-MiniLM-L6-v2) \n", + "meta-llama/Llama-Guard-3-8B (provider's alias: meta-llama/Meta-Llama-Guard-3-8B) \n", "----\n", "Available shields (safety models):\n", "meta-llama/Llama-Guard-3-8B\n", @@ -1367,7 +1213,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "id": "LINBvv8lwTJh", "metadata": { "colab": { @@ -1379,17 +1225,14 @@ }, "outputs": [ { - "output_type": "execute_result", "data": { "text/plain": [ "'meta-llama/Llama-3.1-70B-Instruct'" - ], - "application/vnd.google.colaboratory.intrinsic+json": { - "type": "string" - } + ] }, + "execution_count": 4, "metadata": {}, - "execution_count": 5 + "output_type": "execute_result" } ], "source": [ @@ -1412,7 +1255,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "id": "77c29dba", "metadata": { "colab": { @@ -1423,8 +1266,8 @@ }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Here's a two-sentence poem about a llama:\n", "\n", @@ -1459,12 +1302,31 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "3fdf9df6", "metadata": { "id": "3fdf9df6" }, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[36m> Response: The most famous Prime Minister of England during World War 2 was Winston Churchill. He served as the Prime Minister of the United Kingdom from 1940 to 1945 and again from 1951 to 1955. Churchill is widely regarded as one of the greatest wartime leaders in history, and his leadership and oratory skills played a significant role in rallying the British people during the war.\n", + "\n", + "Churchill's famous speeches, such as \"We shall fight on the beaches\" and \"Their finest hour,\" helped to boost British morale and resistance against the Nazi threat. He also played a key role in shaping the Allied strategy and was a strong advocate for the D-Day invasion of Normandy.\n", + "\n", + "Churchill's leadership during World War 2 has become iconic, and he remains one of the most revered and celebrated figures in British history.\u001b[0m\n", + "\u001b[36m> Response: Winston Churchill's most famous quote is:\n", + "\n", + "\"We shall fight on the beaches, we shall fight on the landing grounds, we shall fight in the fields and in the streets, we shall fight in the hills; we shall never surrender.\"\n", + "\n", + "This quote is from his speech to the House of Commons on June 4, 1940, during the early stages of World War 2, when Nazi Germany was threatening to invade Britain. The speech is known as the \"We Shall Fight on the Beaches\" speech, and it is considered one of the most iconic and inspiring speeches in history.\n", + "\n", + "In the speech, Churchill rallied the British people to prepare for the possibility of a German invasion, and he famously declared that even if the British Empire were to last for a thousand years, the bravery and determination of the British people during this time would be remembered as their \"finest hour.\"\u001b[0m\n" + ] + } + ], "source": [ "from termcolor import cprint\n", "\n", @@ -1515,7 +1377,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "9496f75c", "metadata": { "colab": { @@ -1529,20 +1391,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "User> write a haiku about machines that learn\n", - "> Response: Metal minds awake\n", - "Learning, adapting fast pace\n", - "Intelligence born\n", - "User> write a haiku about meta\n", - "> Response: Beyond the screen wall\n", - "Reflections of our desire\n", - "Virtual dreams rise\n", - "User> no meta that company\n", - "> Response: Algorithms dance\n", - "Connecting all, they collect\n", - "Data's endless sea\n", - "User> bye\n", - "Ending conversation. Goodbye!\n" + "\u001b[36m> Response: Hello, it's nice to meet you. Is there something I can help you with or would you like to chat?\u001b[0m\n", + "\u001b[33mEnding conversation. Goodbye!\u001b[0m\n" ] } ], @@ -1592,7 +1442,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "d119026e", "metadata": { "colab": { @@ -1612,18 +1462,18 @@ "\u001b[0m\u001b[33mThe\u001b[0m\u001b[33m llama\u001b[0m\u001b[33m,\u001b[0m\u001b[33m with\u001b[0m\u001b[33m its\u001b[0m\u001b[33m soft\u001b[0m\u001b[33m and\u001b[0m\u001b[33m wool\u001b[0m\u001b[33mly\u001b[0m\u001b[33m skin\u001b[0m\u001b[33m,\n", "\u001b[0m\u001b[33mA\u001b[0m\u001b[33m symbol\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m region\u001b[0m\u001b[33m's\u001b[0m\u001b[33m myst\u001b[0m\u001b[33mic\u001b[0m\u001b[33m she\u001b[0m\u001b[33men\u001b[0m\u001b[33m.\n", "\n", - "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m eyes\u001b[0m\u001b[33m,\u001b[0m\u001b[33m like\u001b[0m\u001b[33m pools\u001b[0m\u001b[33m of\u001b[0m\u001b[33m calm\u001b[0m\u001b[33m and\u001b[0m\u001b[33m peaceful\u001b[0m\u001b[33m night\u001b[0m\u001b[33m,\n", - "\u001b[0m\u001b[33mReflect\u001b[0m\u001b[33m the\u001b[0m\u001b[33m beauty\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m mountain\u001b[0m\u001b[33m's\u001b[0m\u001b[33m might\u001b[0m\u001b[33m,\n", - "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m ears\u001b[0m\u001b[33m,\u001b[0m\u001b[33m a\u001b[0m\u001b[33m-t\u001b[0m\u001b[33mwitch\u001b[0m\u001b[33m with\u001b[0m\u001b[33m every\u001b[0m\u001b[33m sound\u001b[0m\u001b[33m and\u001b[0m\u001b[33m sight\u001b[0m\u001b[33m,\n", - "\u001b[0m\u001b[33mAs\u001b[0m\u001b[33m if\u001b[0m\u001b[33m it\u001b[0m\u001b[33m listens\u001b[0m\u001b[33m to\u001b[0m\u001b[33m the\u001b[0m\u001b[33m wind\u001b[0m\u001b[33m's\u001b[0m\u001b[33m soft\u001b[0m\u001b[33m light\u001b[0m\u001b[33m.\n", + "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m eyes\u001b[0m\u001b[33m,\u001b[0m\u001b[33m like\u001b[0m\u001b[33m darkest\u001b[0m\u001b[33m night\u001b[0m\u001b[33m,\u001b[0m\u001b[33m with\u001b[0m\u001b[33m wisdom\u001b[0m\u001b[33m shine\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mReflect\u001b[0m\u001b[33ming\u001b[0m\u001b[33m ancient\u001b[0m\u001b[33m knowledge\u001b[0m\u001b[33m,\u001b[0m\u001b[33m passed\u001b[0m\u001b[33m down\u001b[0m\u001b[33m line\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m ears\u001b[0m\u001b[33m,\u001b[0m\u001b[33m like\u001b[0m\u001b[33m satellite\u001b[0m\u001b[33m dishes\u001b[0m\u001b[33m,\u001b[0m\u001b[33m fine\u001b[0m\u001b[33m and\u001b[0m\u001b[33m bright\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mListening\u001b[0m\u001b[33m to\u001b[0m\u001b[33m the\u001b[0m\u001b[33m whispers\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m wind\u001b[0m\u001b[33m's\u001b[0m\u001b[33m design\u001b[0m\u001b[33m.\n", "\n", - "\u001b[0m\u001b[33mWith\u001b[0m\u001b[33m steps\u001b[0m\u001b[33m that\u001b[0m\u001b[33m glide\u001b[0m\u001b[33m,\u001b[0m\u001b[33m like\u001b[0m\u001b[33m a\u001b[0m\u001b[33m slow\u001b[0m\u001b[33m-moving\u001b[0m\u001b[33m stream\u001b[0m\u001b[33m,\n", - "\u001b[0m\u001b[33mIt\u001b[0m\u001b[33m navig\u001b[0m\u001b[33mates\u001b[0m\u001b[33m the\u001b[0m\u001b[33m rocky\u001b[0m\u001b[33m,\u001b[0m\u001b[33m winding\u001b[0m\u001b[33m dream\u001b[0m\u001b[33m,\n", - "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m soft\u001b[0m\u001b[33m hum\u001b[0m\u001b[33m,\u001b[0m\u001b[33m a\u001b[0m\u001b[33m soothing\u001b[0m\u001b[33m melody\u001b[0m\u001b[33m,\u001b[0m\u001b[33m it\u001b[0m\u001b[33m seems\u001b[0m\u001b[33m,\n", - "\u001b[0m\u001b[33mA\u001b[0m\u001b[33m l\u001b[0m\u001b[33mull\u001b[0m\u001b[33maby\u001b[0m\u001b[33m that\u001b[0m\u001b[33m cal\u001b[0m\u001b[33mms\u001b[0m\u001b[33m the\u001b[0m\u001b[33m heart\u001b[0m\u001b[33m's\u001b[0m\u001b[33m wild\u001b[0m\u001b[33m theme\u001b[0m\u001b[33m.\n", + "\u001b[0m\u001b[33mWith\u001b[0m\u001b[33m steps\u001b[0m\u001b[33m that\u001b[0m\u001b[33m barely\u001b[0m\u001b[33m touch\u001b[0m\u001b[33m the\u001b[0m\u001b[33m mountain\u001b[0m\u001b[33m ground\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mIt\u001b[0m\u001b[33m gl\u001b[0m\u001b[33mides\u001b[0m\u001b[33m,\u001b[0m\u001b[33m a\u001b[0m\u001b[33m ghost\u001b[0m\u001b[33mly\u001b[0m\u001b[33m appar\u001b[0m\u001b[33mition\u001b[0m\u001b[33m,\u001b[0m\u001b[33m sound\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mIts\u001b[0m\u001b[33m soft\u001b[0m\u001b[33m hum\u001b[0m\u001b[33m,\u001b[0m\u001b[33m a\u001b[0m\u001b[33m l\u001b[0m\u001b[33mull\u001b[0m\u001b[33maby\u001b[0m\u001b[33m,\u001b[0m\u001b[33m that\u001b[0m\u001b[33m soo\u001b[0m\u001b[33mthes\u001b[0m\u001b[33m the\u001b[0m\u001b[33m soul\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mAs\u001b[0m\u001b[33m it\u001b[0m\u001b[33m travers\u001b[0m\u001b[33mes\u001b[0m\u001b[33m the\u001b[0m\u001b[33m rugged\u001b[0m\u001b[33m,\u001b[0m\u001b[33m rocky\u001b[0m\u001b[33m role\u001b[0m\u001b[33m.\n", "\n", - "\u001b[0m\u001b[33mAnd\u001b[0m\u001b[33m as\u001b[0m\u001b[33m it\u001b[0m\u001b[33m walks\u001b[0m\u001b[33m,\u001b[0m\u001b[33m its\u001b[0m\u001b[33m beauty\u001b[0m\u001b[33m we\u001b[0m\u001b[33m behold\u001b[0m\u001b[33m,\n", - "\u001b[0m\u001b[33mA\u001b[0m\u001b[33m treasure\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m And\u001b[0m\u001b[33mes\u001b[0m\u001b[33m,\u001b[0m\u001b[33m young\u001b[0m\u001b[33m and\u001b[0m\u001b[33m old\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n" + "\u001b[0m\u001b[33mAnd\u001b[0m\u001b[33m when\u001b[0m\u001b[33m it\u001b[0m\u001b[33m stops\u001b[0m\u001b[33m,\u001b[0m\u001b[33m and\u001b[0m\u001b[33m looks\u001b[0m\u001b[33m,\u001b[0m\u001b[33m with\u001b[0m\u001b[33m gentle\u001b[0m\u001b[33m gaze\u001b[0m\u001b[33m,\n", + "\u001b[0m\u001b[33mIt\u001b[0m\u001b[33m seems\u001b[0m\u001b[33m to\u001b[0m\u001b[33m hold\u001b[0m\u001b[33m the\u001b[0m\u001b[33m secrets\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m And\u001b[0m\u001b[33mean\u001b[0m\u001b[33m ways\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n" ] } ], @@ -1658,7 +1508,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "axdQIRaJCYAV", "metadata": { "colab": { @@ -1669,19 +1519,6 @@ "outputId": "a5ef1f54-37df-446e-e21b-cddddaf95f84" }, "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/Users/dineshyv/miniconda3/envs/stack/lib/python3.10/site-packages/pydantic/main.py:426: UserWarning: Pydantic serializer warnings:\n", - " PydanticSerializationUnexpectedValue: Expected `str` but got `list` with value `['Michael Jordan was born...ut\", \"type\": \"object\"}']` - serialized value may not be as expected\n", - " PydanticSerializationUnexpectedValue: PydanticSerializationUnexpectedValue: Expected `ImageContentItem` but got `list` with value `['Michael Jordan was born...ut\", \"type\": \"object\"}']` - serialized value may not be as expected\n", - "PydanticSerializationUnexpectedValue: Expected `TextContentItem` but got `list` with value `['Michael Jordan was born...ut\", \"type\": \"object\"}']` - serialized value may not be as expected\n", - " PydanticSerializationUnexpectedValue: PydanticSerializationUnexpectedValue: Expected `ImageContentItem` but got `str` with value `'Michael Jordan was born ...tion into JSON for me. '` - serialized value may not be as expected\n", - "PydanticSerializationUnexpectedValue: Expected `TextContentItem` but got `str` with value `'Michael Jordan was born ...tion into JSON for me. '` - serialized value may not be as expected\n", - " return self.__pydantic_serializer__.to_python(\n" - ] - }, { "data": { "text/html": [ @@ -1748,7 +1585,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "sUJKJxvAFCaI", "metadata": { "colab": { @@ -1927,7 +1764,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "MpMXiMCv97X5", "metadata": { "colab": { @@ -2044,7 +1881,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 12, "id": "WS8Gu5b0APHs", "metadata": { "colab": { @@ -2063,7 +1900,7 @@ "\u001b[30m\u001b[0m\u001b[32mUser> Which teams played in the NBA western conference finals of 2024\u001b[0m\n", "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mN\u001b[0m\u001b[36mBA\u001b[0m\u001b[36m Western\u001b[0m\u001b[36m Conference\u001b[0m\u001b[36m Finals\u001b[0m\u001b[36m \u001b[0m\u001b[36m202\u001b[0m\u001b[36m4\u001b[0m\u001b[36m teams\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\u001b[0m\n", - "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.9310187, \"raw_content\": null}, {\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.8914433, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.8884594, \"raw_content\": null}, {\"title\": \"NBA Conference Finals Schedule: Full List of Games & Results\", \"url\": \"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\", \"content\": \"The 2024 NBA conference finals matchups are set. Here's the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\", \"score\": 0.850382, \"raw_content\": null}, {\"title\": \"2024 NBA Western Conference playoff bracket - Basketnews.com\", \"url\": \"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\", \"content\": \"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\", \"score\": 0.8473754, \"raw_content\": null}]}\u001b[0m\n", + "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.9310187, \"raw_content\": null}, {\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.8914433, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.8884594, \"raw_content\": null}, {\"title\": \"NBA Conference Finals Schedule: Full List of Games & Results\", \"url\": \"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\", \"content\": \"The 2024 NBA conference finals matchups are set. Here's the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\", \"score\": 0.850382, \"raw_content\": null}, {\"title\": \"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\", \"url\": \"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\", \"content\": \"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\", \"score\": 0.8194462, \"raw_content\": null}]}\u001b[0m\n", "\u001b[33minference> \u001b[0m\u001b[33mThe\u001b[0m\u001b[33m teams\u001b[0m\u001b[33m that\u001b[0m\u001b[33m played\u001b[0m\u001b[33m in\u001b[0m\u001b[33m the\u001b[0m\u001b[33m NBA\u001b[0m\u001b[33m Western\u001b[0m\u001b[33m Conference\u001b[0m\u001b[33m Finals\u001b[0m\u001b[33m of\u001b[0m\u001b[33m \u001b[0m\u001b[33m202\u001b[0m\u001b[33m4\u001b[0m\u001b[33m were\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Dallas\u001b[0m\u001b[33m Mavericks\u001b[0m\u001b[33m and\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Minnesota\u001b[0m\u001b[33m Timber\u001b[0m\u001b[33mw\u001b[0m\u001b[33molves\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[30m\u001b[0m" ] @@ -2121,7 +1958,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "GvLWltzZCNkg", "metadata": { "colab": { @@ -2195,60 +2032,11 @@ }, "outputs": [ { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "3e764c00c08942caa2ccb6b92ee60a4e", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Batches: 0%| | 0/1 [00:00 Tool:query_memory Args:{}\u001b[0m\n", - "\u001b[36mtool_execution> fetched 11069 bytes from memory\u001b[0m\n", - "\u001b[33minference> \u001b[0m\u001b[33mHere\u001b[0m\u001b[33m are\u001b[0m\u001b[33m the\u001b[0m\u001b[33m top\u001b[0m\u001b[33m \u001b[0m\u001b[33m5\u001b[0m\u001b[33m topics\u001b[0m\u001b[33m that\u001b[0m\u001b[33m were\u001b[0m\u001b[33m explained\u001b[0m\u001b[33m:\n", + "\u001b[32mtool_execution> Tool:query_from_memory Args:{}\u001b[0m\n", + "\u001b[36mtool_execution> fetched 10913 bytes from memory\u001b[0m\n", + "\u001b[33minference> \u001b[0m" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\u001b[33mHere\u001b[0m\u001b[33m are\u001b[0m\u001b[33m the\u001b[0m\u001b[33m top\u001b[0m\u001b[33m \u001b[0m\u001b[33m5\u001b[0m\u001b[33m topics\u001b[0m\u001b[33m that\u001b[0m\u001b[33m were\u001b[0m\u001b[33m explained\u001b[0m\u001b[33m:\n", "\n", - "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Fine\u001b[0m\u001b[33m-t\u001b[0m\u001b[33muning\u001b[0m\u001b[33m a\u001b[0m\u001b[33m model\u001b[0m\u001b[33m to\u001b[0m\u001b[33m expect\u001b[0m\u001b[33m a\u001b[0m\u001b[33m certain\u001b[0m\u001b[33m prompt\u001b[0m\u001b[33m structure\u001b[0m\u001b[33m on\u001b[0m\u001b[33m inference\u001b[0m\u001b[33m for\u001b[0m\u001b[33m a\u001b[0m\u001b[33m specific\u001b[0m\u001b[33m task\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Fine\u001b[0m\u001b[33m-t\u001b[0m\u001b[33muning\u001b[0m\u001b[33m on\u001b[0m\u001b[33m a\u001b[0m\u001b[33m custom\u001b[0m\u001b[33m chat\u001b[0m\u001b[33m dataset\u001b[0m\u001b[33m\n", "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Token\u001b[0m\u001b[33mizing\u001b[0m\u001b[33m prompt\u001b[0m\u001b[33m templates\u001b[0m\u001b[33m and\u001b[0m\u001b[33m special\u001b[0m\u001b[33m tokens\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Using\u001b[0m\u001b[33m the\u001b[0m\u001b[33m L\u001b[0m\u001b[33mlama\u001b[0m\u001b[33m2\u001b[0m\u001b[33mChat\u001b[0m\u001b[33mTemplate\u001b[0m\u001b[33m class\u001b[0m\u001b[33m to\u001b[0m\u001b[33m format\u001b[0m\u001b[33m messages\u001b[0m\u001b[33m\n", - "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Token\u001b[0m\u001b[33mizing\u001b[0m\u001b[33m examples\u001b[0m\u001b[33m with\u001b[0m\u001b[33m the\u001b[0m\u001b[33m L\u001b[0m\u001b[33mlama\u001b[0m\u001b[33m2\u001b[0m\u001b[33m tokenizer\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Fine\u001b[0m\u001b[33m-t\u001b[0m\u001b[33muning\u001b[0m\u001b[33m on\u001b[0m\u001b[33m a\u001b[0m\u001b[33m custom\u001b[0m\u001b[33m chat\u001b[0m\u001b[33m dataset\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Using\u001b[0m\u001b[33m the\u001b[0m\u001b[33m L\u001b[0m\u001b[33mlama\u001b[0m\u001b[33m2\u001b[0m\u001b[33mChat\u001b[0m\u001b[33mTemplate\u001b[0m\u001b[33m class\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Formatting\u001b[0m\u001b[33m messages\u001b[0m\u001b[33m with\u001b[0m\u001b[33m the\u001b[0m\u001b[33m L\u001b[0m\u001b[33mlama\u001b[0m\u001b[33m2\u001b[0m\u001b[33mChat\u001b[0m\u001b[33mTemplate\u001b[0m\u001b[33m class\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33m•\u001b[0m\u001b[33m Creating\u001b[0m\u001b[33m a\u001b[0m\u001b[33m custom\u001b[0m\u001b[33m dataset\u001b[0m\u001b[33m for\u001b[0m\u001b[33m fine\u001b[0m\u001b[33m-t\u001b[0m\u001b[33muning\u001b[0m\u001b[33m L\u001b[0m\u001b[33mlama\u001b[0m\u001b[33m3\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[30m\u001b[0m" ] } @@ -2294,7 +2090,7 @@ "from llama_stack_client.lib.agents.event_logger import EventLogger\n", "from llama_stack_client.types.agent_create_params import AgentConfig\n", "from termcolor import cprint\n", - "from llama_stack_client.types.memory_insert_params import Document\n", + "from llama_stack_client.types.tool_runtime import DocumentParam as Document\n", "\n", "urls = [\"chat.rst\", \"llama3.rst\", \"datasets.rst\", \"lora_finetune.rst\"]\n", "documents = [\n", @@ -2306,19 +2102,17 @@ " )\n", " for i, url in enumerate(urls)\n", "]\n", - "memory_bank_id = \"test-memory-bank\"\n", - "client.memory_banks.register(\n", - " memory_bank_id=memory_bank_id,\n", - " params={\n", - " \"memory_bank_type\": \"vector\",\n", - " \"embedding_model\": \"all-MiniLM-L6-v2\",\n", - " \"chunk_size_in_tokens\": 512,\n", - " \"overlap_size_in_tokens\": 64,\n", - " },\n", + "\n", + "vector_db_id = \"test-vector-db\"\n", + "client.vector_dbs.register(\n", + " vector_db_id=vector_db_id,\n", + " embedding_model=\"all-MiniLM-L6-v2\",\n", + " embedding_dimension=384,\n", ")\n", - "client.memory.insert(\n", - " bank_id=memory_bank_id,\n", + "client.tool_runtime.rag_tool.insert(\n", " documents=documents,\n", + " vector_db_id=vector_db_id,\n", + " chunk_size_in_tokens=512,\n", ")\n", "agent_config = AgentConfig(\n", " model=model_id,\n", @@ -2328,7 +2122,7 @@ " {\n", " \"name\": \"builtin::memory\",\n", " \"args\" : {\n", - " \"memory_bank_ids\": [memory_bank_id],\n", + " \"vector_db_ids\": [vector_db_id],\n", " }\n", " }\n", " ],\n", @@ -2362,7 +2156,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "GvVRuhO-GOov", "metadata": { "colab": { @@ -2377,63 +2171,80 @@ "name": "stdout", "output_type": "stream", "text": [ - "User> Here is a csv, can you describe it?\n", - "inference> import pandas as pd\n", - "# Load data\n", - "df = pd.read_csv(\"/tmp/tmpvzjigv7g/n2OzlTWhinflation.csv\")\n", - "# Rows\n", - "print(\"Number of rows and columns in the data:\", df.shape)\n", - "# Columns\n", - "print(\"Columns of the data are:\", len(df.columns))\n", - "# Column names\n", - "print(\"Columns of the data are:\", df.columns)\n", - "# Column dtypes\n", - "print(\"Datatype of the columns are:\", df.dtypes)\n", - "tool_execution> Tool:code_interpreter Args:{'code': 'import pandas as pd\\n# Load data\\ndf = pd.read_csv(\"/tmp/tmpvzjigv7g/n2OzlTWhinflation.csv\")\\n# Rows\\nprint(\"Number of rows and columns in the data:\", df.shape)\\n# Columns\\nprint(\"Columns of the data are:\", len(df.columns))\\n# Column names\\nprint(\"Columns of the data are:\", df.columns)\\n# Column dtypes\\nprint(\"Datatype of the columns are:\", df.dtypes)'}\n", - "tool_execution> Tool:code_interpreter Response:completed\n", + "\u001b[32mUser> Here is a csv, can you describe it?\u001b[0m\n", + "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mimport\u001b[0m\u001b[36m pandas\u001b[0m\u001b[36m as\u001b[0m\u001b[36m pd\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36m#\u001b[0m\u001b[36m Load\u001b[0m\u001b[36m data\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36mdf\u001b[0m\u001b[36m =\u001b[0m\u001b[36m pd\u001b[0m\u001b[36m.read\u001b[0m\u001b[36m_csv\u001b[0m\u001b[36m('/\u001b[0m\u001b[36mvar\u001b[0m\u001b[36m/f\u001b[0m\u001b[36molders\u001b[0m\u001b[36m/m\u001b[0m\u001b[36mj\u001b[0m\u001b[36m/t\u001b[0m\u001b[36m_st\u001b[0m\u001b[36mv\u001b[0m\u001b[36m1\u001b[0m\u001b[36mys\u001b[0m\u001b[36m763\u001b[0m\u001b[36m7\u001b[0m\u001b[36mv\u001b[0m\u001b[36mq\u001b[0m\u001b[36mf\u001b[0m\u001b[36m2\u001b[0m\u001b[36m_b\u001b[0m\u001b[36m4\u001b[0m\u001b[36my\u001b[0m\u001b[36mf\u001b[0m\u001b[36m67\u001b[0m\u001b[36mm\u001b[0m\u001b[36m000\u001b[0m\u001b[36m0\u001b[0m\u001b[36mgn\u001b[0m\u001b[36m/T\u001b[0m\u001b[36m/tmp\u001b[0m\u001b[36mq\u001b[0m\u001b[36m2\u001b[0m\u001b[36mw\u001b[0m\u001b[36mjj\u001b[0m\u001b[36mmg\u001b[0m\u001b[36mf\u001b[0m\u001b[36m/s\u001b[0m\u001b[36mQ\u001b[0m\u001b[36mAm\u001b[0m\u001b[36muk\u001b[0m\u001b[36mV\u001b[0m\u001b[36mbin\u001b[0m\u001b[36mflation\u001b[0m\u001b[36m.csv\u001b[0m\u001b[36m')\n", + "\u001b[0m\u001b[36m#\u001b[0m\u001b[36m Set\u001b[0m\u001b[36m options\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36mpd\u001b[0m\u001b[36m.set\u001b[0m\u001b[36m_option\u001b[0m\u001b[36m('\u001b[0m\u001b[36mdisplay\u001b[0m\u001b[36m.max\u001b[0m\u001b[36m_columns\u001b[0m\u001b[36m',\u001b[0m\u001b[36m None\u001b[0m\u001b[36m)\n", + "\u001b[0m\u001b[36mpd\u001b[0m\u001b[36m.set\u001b[0m\u001b[36m_option\u001b[0m\u001b[36m('\u001b[0m\u001b[36mdisplay\u001b[0m\u001b[36m.max\u001b[0m\u001b[36m_rows\u001b[0m\u001b[36m',\u001b[0m\u001b[36m None\u001b[0m\u001b[36m)\n", + "\u001b[0m\u001b[36m#\u001b[0m\u001b[36m Describe\u001b[0m\u001b[36m the\u001b[0m\u001b[36m data\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36mprint\u001b[0m\u001b[36m(df\u001b[0m\u001b[36m.describe\u001b[0m\u001b[36m())\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[32mtool_execution> Tool:code_interpreter Args:{'code': \"import pandas as pd\\n# Load data\\ndf = pd.read_csv('/var/folders/mj/t_stv1ys7637vqf2_b4yf67m0000gn/T/tmpq2wjjmgf/sQAmukVbinflation.csv')\\n# Set options\\npd.set_option('display.max_columns', None)\\npd.set_option('display.max_rows', None)\\n# Describe the data\\nprint(df.describe())\"}\u001b[0m\n", + "\u001b[32mtool_execution> Tool:code_interpreter Response:error\n", "[stdout]\n", - "Number of rows and columns in the data: (10, 13)\n", - "Columns of the data are: 13\n", - "Columns of the data are: Index(['Year', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',\n", - " 'Oct', 'Nov', 'Dec'],\n", - " dtype='object')\n", - "Datatype of the columns are: Year int64\n", - "Jan float64\n", - "Feb float64\n", - "Mar float64\n", - "Apr float64\n", - "May float64\n", - "Jun float64\n", - "Jul float64\n", - "Aug float64\n", - "Sep float64\n", - "Oct float64\n", - "Nov float64\n", - "Dec float64\n", - "dtype: object\n", + "[Errno 2] No such file or directory: 'bwrap'\n", "[/stdout]\n", - "inference> The csv file contains 10 rows and 13 columns. The columns are named 'Year', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'. The data types of the columns are all float64, indicating that the data is numeric. The 'Year' column is of type int64, suggesting that it contains integer values. The remaining 12 columns contain floating point numbers.\n", - "User> Plot average yearly inflation as a time series\n", - "inference> import pandas as pd\n", - "import matplotlib.pyplot as plt\n", + "[stderr]\n", + "[Errno 2] No such file or directory: 'bwrap'\n", + "[/stderr]\u001b[0m\n", + "\u001b[33minference> \u001b[0m\u001b[33mIt\u001b[0m\u001b[33m seems\u001b[0m\u001b[33m that\u001b[0m\u001b[33m there\u001b[0m\u001b[33m was\u001b[0m\u001b[33m an\u001b[0m\u001b[33m issue\u001b[0m\u001b[33m with\u001b[0m\u001b[33m accessing\u001b[0m\u001b[33m the\u001b[0m\u001b[33m file\u001b[0m\u001b[33m.\u001b[0m\u001b[33m I\u001b[0m\u001b[33m'm\u001b[0m\u001b[33m a\u001b[0m\u001b[33m large\u001b[0m\u001b[33m language\u001b[0m\u001b[33m model\u001b[0m\u001b[33m,\u001b[0m\u001b[33m I\u001b[0m\u001b[33m don\u001b[0m\u001b[33m't\u001b[0m\u001b[33m have\u001b[0m\u001b[33m the\u001b[0m\u001b[33m ability\u001b[0m\u001b[33m to\u001b[0m\u001b[33m access\u001b[0m\u001b[33m or\u001b[0m\u001b[33m read\u001b[0m\u001b[33m files\u001b[0m\u001b[33m from\u001b[0m\u001b[33m your\u001b[0m\u001b[33m local\u001b[0m\u001b[33m system\u001b[0m\u001b[33m.\u001b[0m\u001b[33m However\u001b[0m\u001b[33m,\u001b[0m\u001b[33m I\u001b[0m\u001b[33m can\u001b[0m\u001b[33m guide\u001b[0m\u001b[33m you\u001b[0m\u001b[33m on\u001b[0m\u001b[33m how\u001b[0m\u001b[33m to\u001b[0m\u001b[33m describe\u001b[0m\u001b[33m a\u001b[0m\u001b[33m CSV\u001b[0m\u001b[33m file\u001b[0m\u001b[33m using\u001b[0m\u001b[33m Python\u001b[0m\u001b[33m's\u001b[0m\u001b[33m pandas\u001b[0m\u001b[33m library\u001b[0m\u001b[33m.\n", "\n", - "# Load data\n", - "df = pd.read_csv(\"/tmp/tmpvzjigv7g/n2OzlTWhinflation.csv\")\n", + "\u001b[0m\u001b[33mTo\u001b[0m\u001b[33m describe\u001b[0m\u001b[33m a\u001b[0m\u001b[33m CSV\u001b[0m\u001b[33m file\u001b[0m\u001b[33m,\u001b[0m\u001b[33m you\u001b[0m\u001b[33m can\u001b[0m\u001b[33m use\u001b[0m\u001b[33m the\u001b[0m\u001b[33m `\u001b[0m\u001b[33mp\u001b[0m\u001b[33mandas\u001b[0m\u001b[33m`\u001b[0m\u001b[33m library\u001b[0m\u001b[33m in\u001b[0m\u001b[33m Python\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Here\u001b[0m\u001b[33m's\u001b[0m\u001b[33m a\u001b[0m\u001b[33m step\u001b[0m\u001b[33m-by\u001b[0m\u001b[33m-step\u001b[0m\u001b[33m guide\u001b[0m\u001b[33m:\n", "\n", - "# Calculate average yearly inflation\n", - "df['Average'] = df[['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']].mean(axis=1)\n", + "\u001b[0m\u001b[33m1\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Install\u001b[0m\u001b[33m the\u001b[0m\u001b[33m `\u001b[0m\u001b[33mp\u001b[0m\u001b[33mandas\u001b[0m\u001b[33m`\u001b[0m\u001b[33m library\u001b[0m\u001b[33m if\u001b[0m\u001b[33m you\u001b[0m\u001b[33m haven\u001b[0m\u001b[33m't\u001b[0m\u001b[33m already\u001b[0m\u001b[33m:\u001b[0m\u001b[33m `\u001b[0m\u001b[33mpip\u001b[0m\u001b[33m install\u001b[0m\u001b[33m pandas\u001b[0m\u001b[33m`\n", + "\u001b[0m\u001b[33m2\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Import\u001b[0m\u001b[33m the\u001b[0m\u001b[33m `\u001b[0m\u001b[33mp\u001b[0m\u001b[33mandas\u001b[0m\u001b[33m`\u001b[0m\u001b[33m library\u001b[0m\u001b[33m:\u001b[0m\u001b[33m `\u001b[0m\u001b[33mimport\u001b[0m\u001b[33m pandas\u001b[0m\u001b[33m as\u001b[0m\u001b[33m pd\u001b[0m\u001b[33m`\n", + "\u001b[0m\u001b[33m3\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Load\u001b[0m\u001b[33m the\u001b[0m\u001b[33m CSV\u001b[0m\u001b[33m file\u001b[0m\u001b[33m into\u001b[0m\u001b[33m a\u001b[0m\u001b[33m DataFrame\u001b[0m\u001b[33m:\u001b[0m\u001b[33m `\u001b[0m\u001b[33mdf\u001b[0m\u001b[33m =\u001b[0m\u001b[33m pd\u001b[0m\u001b[33m.read\u001b[0m\u001b[33m_csv\u001b[0m\u001b[33m('\u001b[0m\u001b[33myour\u001b[0m\u001b[33m_file\u001b[0m\u001b[33m.csv\u001b[0m\u001b[33m')\u001b[0m\u001b[33m`\n", + "\u001b[0m\u001b[33m4\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Use\u001b[0m\u001b[33m the\u001b[0m\u001b[33m `\u001b[0m\u001b[33mdescribe\u001b[0m\u001b[33m()`\u001b[0m\u001b[33m method\u001b[0m\u001b[33m to\u001b[0m\u001b[33m get\u001b[0m\u001b[33m a\u001b[0m\u001b[33m summary\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m data\u001b[0m\u001b[33m:\u001b[0m\u001b[33m `\u001b[0m\u001b[33mprint\u001b[0m\u001b[33m(df\u001b[0m\u001b[33m.describe\u001b[0m\u001b[33m())\u001b[0m\u001b[33m`\n", "\n", - "# Plot average yearly inflation as a time series\n", - "plt.figure(figsize=(10,6))\n", - "plt.plot(df['Year'], df['Average'])\n", - "plt.title('Average Yearly Inflation')\n", - "plt.xlabel('Year')\n", - "plt.ylabel('Average Inflation')\n", - "plt.grid(True)\n", - "plt.show()\n", - "tool_execution> Tool:code_interpreter Args:{'code': 'import pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Load data\\ndf = pd.read_csv(\"/tmp/tmpvzjigv7g/n2OzlTWhinflation.csv\")\\n\\n# Calculate average yearly inflation\\ndf[\\'Average\\'] = df[[\\'Jan\\', \\'Feb\\', \\'Mar\\', \\'Apr\\', \\'May\\', \\'Jun\\', \\'Jul\\', \\'Aug\\', \\'Sep\\', \\'Oct\\', \\'Nov\\', \\'Dec\\']].mean(axis=1)\\n\\n# Plot average yearly inflation as a time series\\nplt.figure(figsize=(10,6))\\nplt.plot(df[\\'Year\\'], df[\\'Average\\'])\\nplt.title(\\'Average Yearly Inflation\\')\\nplt.xlabel(\\'Year\\')\\nplt.ylabel(\\'Average Inflation\\')\\nplt.grid(True)\\nplt.show()'}\n", - "tool_execution> Tool:code_interpreter Response:completed\n", - "inference> This code calculates the average inflation for each year by taking the mean of the 12 monthly inflation rates. It then plots this average yearly inflation as a time series using matplotlib. The x-axis represents the year and the y-axis represents the average inflation. The plot shows the trend of average yearly inflation over the years.\n" + "\u001b[0m\u001b[33mThis\u001b[0m\u001b[33m will\u001b[0m\u001b[33m give\u001b[0m\u001b[33m you\u001b[0m\u001b[33m a\u001b[0m\u001b[33m summary\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m data\u001b[0m\u001b[33m,\u001b[0m\u001b[33m including\u001b[0m\u001b[33m the\u001b[0m\u001b[33m count\u001b[0m\u001b[33m,\u001b[0m\u001b[33m mean\u001b[0m\u001b[33m,\u001b[0m\u001b[33m standard\u001b[0m\u001b[33m deviation\u001b[0m\u001b[33m,\u001b[0m\u001b[33m minimum\u001b[0m\u001b[33m,\u001b[0m\u001b[33m \u001b[0m\u001b[33m25\u001b[0m\u001b[33mth\u001b[0m\u001b[33m percentile\u001b[0m\u001b[33m,\u001b[0m\u001b[33m \u001b[0m\u001b[33m50\u001b[0m\u001b[33mth\u001b[0m\u001b[33m percentile\u001b[0m\u001b[33m,\u001b[0m\u001b[33m \u001b[0m\u001b[33m75\u001b[0m\u001b[33mth\u001b[0m\u001b[33m percentile\u001b[0m\u001b[33m,\u001b[0m\u001b[33m and\u001b[0m\u001b[33m maximum\u001b[0m\u001b[33m for\u001b[0m\u001b[33m each\u001b[0m\u001b[33m column\u001b[0m\u001b[33m.\n", + "\n", + "\u001b[0m\u001b[33mIf\u001b[0m\u001b[33m you\u001b[0m\u001b[33m provide\u001b[0m\u001b[33m the\u001b[0m\u001b[33m contents\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m CSV\u001b[0m\u001b[33m file\u001b[0m\u001b[33m,\u001b[0m\u001b[33m I\u001b[0m\u001b[33m can\u001b[0m\u001b[33m help\u001b[0m\u001b[33m you\u001b[0m\u001b[33m describe\u001b[0m\u001b[33m it\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[30m\u001b[0m\u001b[32mUser> Plot average yearly inflation as a time series\u001b[0m\n", + "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mimport\u001b[0m\u001b[36m pandas\u001b[0m\u001b[36m as\u001b[0m\u001b[36m pd\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36mimport\u001b[0m\u001b[36m matplotlib\u001b[0m\u001b[36m.pyplot\u001b[0m\u001b[36m as\u001b[0m\u001b[36m plt\u001b[0m\u001b[36m\n", + "\n", + "\u001b[0m\u001b[36m#\u001b[0m\u001b[36m Load\u001b[0m\u001b[36m the\u001b[0m\u001b[36m data\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36mdf\u001b[0m\u001b[36m =\u001b[0m\u001b[36m pd\u001b[0m\u001b[36m.read\u001b[0m\u001b[36m_csv\u001b[0m\u001b[36m('/\u001b[0m\u001b[36mvar\u001b[0m\u001b[36m/f\u001b[0m\u001b[36molders\u001b[0m\u001b[36m/m\u001b[0m\u001b[36mj\u001b[0m\u001b[36m/t\u001b[0m\u001b[36m_st\u001b[0m\u001b[36mv\u001b[0m\u001b[36m1\u001b[0m\u001b[36mys\u001b[0m\u001b[36m763\u001b[0m\u001b[36m7\u001b[0m\u001b[36mv\u001b[0m\u001b[36mq\u001b[0m\u001b[36mf\u001b[0m\u001b[36m2\u001b[0m\u001b[36m_b\u001b[0m\u001b[36m4\u001b[0m\u001b[36my\u001b[0m\u001b[36mf\u001b[0m\u001b[36m67\u001b[0m\u001b[36mm\u001b[0m\u001b[36m000\u001b[0m\u001b[36m0\u001b[0m\u001b[36mgn\u001b[0m\u001b[36m/T\u001b[0m\u001b[36m/tmp\u001b[0m\u001b[36mq\u001b[0m\u001b[36m2\u001b[0m\u001b[36mw\u001b[0m\u001b[36mjj\u001b[0m\u001b[36mmg\u001b[0m\u001b[36mf\u001b[0m\u001b[36m/s\u001b[0m\u001b[36mQ\u001b[0m\u001b[36mAm\u001b[0m\u001b[36muk\u001b[0m\u001b[36mV\u001b[0m\u001b[36mbin\u001b[0m\u001b[36mflation\u001b[0m\u001b[36m.csv\u001b[0m\u001b[36m')\n", + "\n", + "\u001b[0m\u001b[36m#\u001b[0m\u001b[36m Convert\u001b[0m\u001b[36m the\u001b[0m\u001b[36m '\u001b[0m\u001b[36myear\u001b[0m\u001b[36m'\u001b[0m\u001b[36m column\u001b[0m\u001b[36m to\u001b[0m\u001b[36m datetime\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36mdf\u001b[0m\u001b[36m['\u001b[0m\u001b[36myear\u001b[0m\u001b[36m']\u001b[0m\u001b[36m =\u001b[0m\u001b[36m pd\u001b[0m\u001b[36m.to\u001b[0m\u001b[36m_datetime\u001b[0m\u001b[36m(df\u001b[0m\u001b[36m['\u001b[0m\u001b[36myear\u001b[0m\u001b[36m'])\n", + "\n", + "\u001b[0m\u001b[36m#\u001b[0m\u001b[36m Group\u001b[0m\u001b[36m by\u001b[0m\u001b[36m year\u001b[0m\u001b[36m and\u001b[0m\u001b[36m calculate\u001b[0m\u001b[36m the\u001b[0m\u001b[36m average\u001b[0m\u001b[36m inflation\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36maverage\u001b[0m\u001b[36m_in\u001b[0m\u001b[36mflation\u001b[0m\u001b[36m =\u001b[0m\u001b[36m df\u001b[0m\u001b[36m.groupby\u001b[0m\u001b[36m('\u001b[0m\u001b[36myear\u001b[0m\u001b[36m')['\u001b[0m\u001b[36min\u001b[0m\u001b[36mflation\u001b[0m\u001b[36m'].\u001b[0m\u001b[36mmean\u001b[0m\u001b[36m()\n", + "\n", + "\u001b[0m\u001b[36m#\u001b[0m\u001b[36m Plot\u001b[0m\u001b[36m the\u001b[0m\u001b[36m average\u001b[0m\u001b[36m yearly\u001b[0m\u001b[36m inflation\u001b[0m\u001b[36m as\u001b[0m\u001b[36m a\u001b[0m\u001b[36m time\u001b[0m\u001b[36m series\u001b[0m\u001b[36m\n", + "\u001b[0m\u001b[36mplt\u001b[0m\u001b[36m.figure\u001b[0m\u001b[36m(figsize\u001b[0m\u001b[36m=(\u001b[0m\u001b[36m10\u001b[0m\u001b[36m,\u001b[0m\u001b[36m6\u001b[0m\u001b[36m))\n", + "\u001b[0m\u001b[36mplt\u001b[0m\u001b[36m.plot\u001b[0m\u001b[36m(\u001b[0m\u001b[36maverage\u001b[0m\u001b[36m_in\u001b[0m\u001b[36mflation\u001b[0m\u001b[36m.index\u001b[0m\u001b[36m,\u001b[0m\u001b[36m average\u001b[0m\u001b[36m_in\u001b[0m\u001b[36mflation\u001b[0m\u001b[36m.values\u001b[0m\u001b[36m,\u001b[0m\u001b[36m marker\u001b[0m\u001b[36m='\u001b[0m\u001b[36mo\u001b[0m\u001b[36m')\n", + "\u001b[0m\u001b[36mplt\u001b[0m\u001b[36m.title\u001b[0m\u001b[36m('\u001b[0m\u001b[36mAverage\u001b[0m\u001b[36m Year\u001b[0m\u001b[36mly\u001b[0m\u001b[36m In\u001b[0m\u001b[36mflation\u001b[0m\u001b[36m')\n", + "\u001b[0m\u001b[36mplt\u001b[0m\u001b[36m.xlabel\u001b[0m\u001b[36m('\u001b[0m\u001b[36mYear\u001b[0m\u001b[36m')\n", + "\u001b[0m\u001b[36mplt\u001b[0m\u001b[36m.ylabel\u001b[0m\u001b[36m('\u001b[0m\u001b[36mAverage\u001b[0m\u001b[36m In\u001b[0m\u001b[36mflation\u001b[0m\u001b[36m')\n", + "\u001b[0m\u001b[36mplt\u001b[0m\u001b[36m.grid\u001b[0m\u001b[36m(True\u001b[0m\u001b[36m)\n", + "\u001b[0m\u001b[36mplt\u001b[0m\u001b[36m.show\u001b[0m\u001b[36m()\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[32mtool_execution> Tool:code_interpreter Args:{'code': \"import pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Load the data\\ndf = pd.read_csv('/var/folders/mj/t_stv1ys7637vqf2_b4yf67m0000gn/T/tmpq2wjjmgf/sQAmukVbinflation.csv')\\n\\n# Convert the 'year' column to datetime\\ndf['year'] = pd.to_datetime(df['year'])\\n\\n# Group by year and calculate the average inflation\\naverage_inflation = df.groupby('year')['inflation'].mean()\\n\\n# Plot the average yearly inflation as a time series\\nplt.figure(figsize=(10,6))\\nplt.plot(average_inflation.index, average_inflation.values, marker='o')\\nplt.title('Average Yearly Inflation')\\nplt.xlabel('Year')\\nplt.ylabel('Average Inflation')\\nplt.grid(True)\\nplt.show()\"}\u001b[0m\n", + "\u001b[32mtool_execution> Tool:code_interpreter Response:error\n", + "[stdout]\n", + "[Errno 2] No such file or directory: 'bwrap'\n", + "[/stdout]\n", + "[stderr]\n", + "[Errno 2] No such file or directory: 'bwrap'\n", + "[/stderr]\u001b[0m\n", + "\u001b[33minference> \u001b[0m\u001b[33mIt\u001b[0m\u001b[33m seems\u001b[0m\u001b[33m that\u001b[0m\u001b[33m there\u001b[0m\u001b[33m was\u001b[0m\u001b[33m an\u001b[0m\u001b[33m issue\u001b[0m\u001b[33m with\u001b[0m\u001b[33m accessing\u001b[0m\u001b[33m the\u001b[0m\u001b[33m file\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Since\u001b[0m\u001b[33m I\u001b[0m\u001b[33m don\u001b[0m\u001b[33m't\u001b[0m\u001b[33m have\u001b[0m\u001b[33m the\u001b[0m\u001b[33m ability\u001b[0m\u001b[33m to\u001b[0m\u001b[33m access\u001b[0m\u001b[33m or\u001b[0m\u001b[33m read\u001b[0m\u001b[33m files\u001b[0m\u001b[33m from\u001b[0m\u001b[33m your\u001b[0m\u001b[33m local\u001b[0m\u001b[33m system\u001b[0m\u001b[33m,\u001b[0m\u001b[33m I\u001b[0m\u001b[33m'll\u001b[0m\u001b[33m provide\u001b[0m\u001b[33m a\u001b[0m\u001b[33m general\u001b[0m\u001b[33m solution\u001b[0m\u001b[33m.\n", + "\n", + "\u001b[0m\u001b[33mTo\u001b[0m\u001b[33m plot\u001b[0m\u001b[33m the\u001b[0m\u001b[33m average\u001b[0m\u001b[33m yearly\u001b[0m\u001b[33m inflation\u001b[0m\u001b[33m as\u001b[0m\u001b[33m a\u001b[0m\u001b[33m time\u001b[0m\u001b[33m series\u001b[0m\u001b[33m,\u001b[0m\u001b[33m you\u001b[0m\u001b[33m'll\u001b[0m\u001b[33m need\u001b[0m\u001b[33m to\u001b[0m\u001b[33m have\u001b[0m\u001b[33m the\u001b[0m\u001b[33m following\u001b[0m\u001b[33m data\u001b[0m\u001b[33m:\n", + "\n", + "\u001b[0m\u001b[33m-\u001b[0m\u001b[33m A\u001b[0m\u001b[33m column\u001b[0m\u001b[33m with\u001b[0m\u001b[33m the\u001b[0m\u001b[33m year\u001b[0m\u001b[33m\n", + "\u001b[0m\u001b[33m-\u001b[0m\u001b[33m A\u001b[0m\u001b[33m column\u001b[0m\u001b[33m with\u001b[0m\u001b[33m the\u001b[0m\u001b[33m inflation\u001b[0m\u001b[33m rate\u001b[0m\u001b[33m for\u001b[0m\u001b[33m each\u001b[0m\u001b[33m year\u001b[0m\u001b[33m\n", + "\n", + "\u001b[0m\u001b[33mHere\u001b[0m\u001b[33m's\u001b[0m\u001b[33m a\u001b[0m\u001b[33m general\u001b[0m\u001b[33m solution\u001b[0m\u001b[33m:\n", + "\n", + "\u001b[0m\u001b[33m1\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Load\u001b[0m\u001b[33m the\u001b[0m\u001b[33m data\u001b[0m\u001b[33m into\u001b[0m\u001b[33m a\u001b[0m\u001b[33m pandas\u001b[0m\u001b[33m DataFrame\u001b[0m\u001b[33m.\n", + "\u001b[0m\u001b[33m2\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Convert\u001b[0m\u001b[33m the\u001b[0m\u001b[33m '\u001b[0m\u001b[33myear\u001b[0m\u001b[33m'\u001b[0m\u001b[33m column\u001b[0m\u001b[33m to\u001b[0m\u001b[33m datetime\u001b[0m\u001b[33m.\n", + "\u001b[0m\u001b[33m3\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Group\u001b[0m\u001b[33m by\u001b[0m\u001b[33m year\u001b[0m\u001b[33m and\u001b[0m\u001b[33m calculate\u001b[0m\u001b[33m the\u001b[0m\u001b[33m average\u001b[0m\u001b[33m inflation\u001b[0m\u001b[33m.\n", + "\u001b[0m\u001b[33m4\u001b[0m\u001b[33m.\u001b[0m\u001b[33m Plot\u001b[0m\u001b[33m the\u001b[0m\u001b[33m average\u001b[0m\u001b[33m yearly\u001b[0m\u001b[33m inflation\u001b[0m\u001b[33m as\u001b[0m\u001b[33m a\u001b[0m\u001b[33m time\u001b[0m\u001b[33m series\u001b[0m\u001b[33m using\u001b[0m\u001b[33m matplotlib\u001b[0m\u001b[33m.\n", + "\n", + "\u001b[0m\u001b[33mIf\u001b[0m\u001b[33m you\u001b[0m\u001b[33m provide\u001b[0m\u001b[33m the\u001b[0m\u001b[33m contents\u001b[0m\u001b[33m of\u001b[0m\u001b[33m the\u001b[0m\u001b[33m CSV\u001b[0m\u001b[33m file\u001b[0m\u001b[33m,\u001b[0m\u001b[33m I\u001b[0m\u001b[33m can\u001b[0m\u001b[33m help\u001b[0m\u001b[33m you\u001b[0m\u001b[33m plot\u001b[0m\u001b[33m the\u001b[0m\u001b[33m average\u001b[0m\u001b[33m yearly\u001b[0m\u001b[33m inflation\u001b[0m\u001b[33m as\u001b[0m\u001b[33m a\u001b[0m\u001b[33m time\u001b[0m\u001b[33m series\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[30m\u001b[0m" ] } ], @@ -2547,6 +2358,10 @@ }, { "cell_type": "markdown", + "id": "jSfjNN9fMxtm", + "metadata": { + "id": "jSfjNN9fMxtm" + }, "source": [ "### 2.5. Using Model Context Protocol\n", "\n", @@ -2555,18 +2370,12 @@ "In the following steps, we will use the [filesystem tool](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) to explore the files and folders available in the /content directory\n", "\n", "Use xterm module to start a shell to run the MCP server using the `supergateway` tool which can start an MCP tool and serve it over HTTP." - ], - "metadata": { - "id": "jSfjNN9fMxtm" - }, - "id": "jSfjNN9fMxtm" + ] }, { "cell_type": "code", - "source": [ - "!pip install colab-xterm #https://pypi.org/project/colab-xterm/\n", - "%load_ext colabxterm" - ], + "execution_count": 8, + "id": "67fDKVVpNuFb", "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -2574,12 +2383,10 @@ "id": "67fDKVVpNuFb", "outputId": "aec2e3cf-e1c3-4d09-d9dc-c4a2f1327e99" }, - "id": "67fDKVVpNuFb", - "execution_count": 8, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Collecting colab-xterm\n", " Downloading colab_xterm-0.2.0-py3-none-any.whl.metadata (1.2 kB)\n", @@ -2591,23 +2398,23 @@ "Successfully installed colab-xterm-0.2.0\n" ] } + ], + "source": [ + "!pip install colab-xterm #https://pypi.org/project/colab-xterm/\n", + "%load_ext colabxterm" ] }, { "cell_type": "code", - "source": [ - "\n", - "%xterm\n", - "# touch /content/foo\n", - "# touch /content/bar\n", - "# npx -y supergateway --port 8000 --stdio 'npx -y @modelcontextprotocol/server-filesystem /content'" - ], + "execution_count": 9, + "id": "giIA2M-ANUIM", "metadata": { "colab": { + "base_uri": "https://localhost:8080/", + "height": 839, "resources": { "https://localhost:10000/": { "data": "PCFkb2N0eXBlIGh0bWw+PGh0bWw+PGhlYWQ+PG1ldGEgY2hhcnNldD0idXRmLTgiLz48c2NyaXB0IGRlZmVyPSJkZWZlciIgc3JjPSJtYWluLmpzIj48L3NjcmlwdD48L2hlYWQ+PGJvZHk+PGRpdiBpZD0idGVybWluYWwiPjwvZGl2PjwvYm9keT48L2h0bWw+", - "ok": true, "headers": [ [ "content-length", @@ -2618,12 +2425,284 @@ "text/html; charset=UTF-8" ] ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Aw==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/DA==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/DQ==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/G1syMDB+bnB4IC15IHN1cGVyZ2F0ZXdheSAtLXBvcnQgODAwMCAtLXN0ZGlvICducHggLXkgQG1vZGVsY29udGV4dHByb3RvY29sL3NlcnZlci1maWxlc3lzdGVtIC9jb250ZW50JxtbMjAxfg==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/G1tB": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/IA==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Y2g=": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/YXI=": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Yg==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Yw==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/Zg==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/aCA=": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/b3U=": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/bw0=": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/bw==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/dA==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, + "status": 200, + "status_text": "" + }, + "https://localhost:10000/in/dQ==": { + "data": "", + "headers": [ + [ + "content-length", + "0" + ], + [ + "content-type", + "text/html; charset=UTF-8" + ] + ], + "ok": true, "status": 200, "status_text": "" }, "https://localhost:10000/main.js": { "data": "LyohIEZvciBsaWNlbnNlIGluZm9ybWF0aW9uIHBsZWFzZSBzZWUgbWFpbi5qcy5MSUNFTlNFLnR4dCAqLwooKCk9Pnt2YXIgZT17MTAyOihlLHQscik9PnsidXNlIHN0cmljdCI7ci5kKHQse1o6KCk9PmF9KTt2YXIgaT1yKDgxKSxuPXIubihpKSxvPXIoNjQ1KSxzPXIubihvKSgpKG4oKSk7cy5wdXNoKFtlLmlkLCcvKipcbiAqIENvcHlyaWdodCAoYykgMjAxNCBUaGUgeHRlcm0uanMgYXV0aG9ycy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbiAqIENvcHlyaWdodCAoYykgMjAxMi0yMDEzLCBDaHJpc3RvcGhlciBKZWZmcmV5IChNSVQgTGljZW5zZSlcbiAqIGh0dHBzOi8vZ2l0aHViLmNvbS9jaGpqL3Rlcm0uanNcbiAqIEBsaWNlbnNlIE1JVFxuICpcbiAqIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHlcbiAqIG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlICJTb2Z0d2FyZSIpLCB0byBkZWFsXG4gKiBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGUgcmlnaHRzXG4gKiB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsXG4gKiBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXNcbiAqIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6XG4gKlxuICogVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW5cbiAqIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuICpcbiAqIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCAiQVMgSVMiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SXG4gKiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbiAqIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRVxuICogQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUlxuICogTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbiAqIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU5cbiAqIFRIRSBTT0ZUV0FSRS5cbiAqXG4gKiBPcmlnaW5hbGx5IGZvcmtlZCBmcm9tICh3aXRoIHRoZSBhdXRob3JcJ3MgcGVybWlzc2lvbik6XG4gKiAgIEZhYnJpY2UgQmVsbGFyZFwncyBqYXZhc2NyaXB0IHZ0MTAwIGZvciBqc2xpbnV4OlxuICogICBodHRwOi8vYmVsbGFyZC5vcmcvanNsaW51eC9cbiAqICAgQ29weXJpZ2h0IChjKSAyMDExIEZhYnJpY2UgQmVsbGFyZFxuICogICBUaGUgb3JpZ2luYWwgZGVzaWduIHJlbWFpbnMuIFRoZSB0ZXJtaW5hbCBpdHNlbGZcbiAqICAgaGFzIGJlZW4gZXh0ZW5kZWQgdG8gaW5jbHVkZSB4dGVybSBDU0kgY29kZXMsIGFtb25nXG4gKiAgIG90aGVyIGZlYXR1cmVzLlxuICovXG5cbi8qKlxuICogIERlZmF1bHQgc3R5bGVzIGZvciB4dGVybS5qc1xuICovXG5cbi54dGVybSB7XG4gICAgcG9zaXRpb246IHJlbGF0aXZlO1xuICAgIC1tb3otdXNlci1zZWxlY3Q6IG5vbmU7XG4gICAgICAgICB1c2VyLXNlbGVjdDogbm9uZTtcbiAgICAtbXMtdXNlci1zZWxlY3Q6IG5vbmU7XG4gICAgLXdlYmtpdC11c2VyLXNlbGVjdDogbm9uZTtcbn1cblxuLnh0ZXJtLmZvY3VzLFxuLnh0ZXJtOmZvY3VzIHtcbiAgICBvdXRsaW5lOiBub25lO1xufVxuXG4ueHRlcm0gLnh0ZXJtLWhlbHBlcnMge1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICB0b3A6IDA7XG4gICAgLyoqXG4gICAgICogVGhlIHotaW5kZXggb2YgdGhlIGhlbHBlcnMgbXVzdCBiZSBoaWdoZXIgdGhhbiB0aGUgY2FudmFzZXMgaW4gb3JkZXIgZm9yXG4gICAgICogSU1FcyB0byBhcHBlYXIgb24gdG9wLlxuICAgICAqL1xuICAgIHotaW5kZXg6IDU7XG59XG5cbi54dGVybSAueHRlcm0taGVscGVyLXRleHRhcmVhIHtcbiAgICBwYWRkaW5nOiAwO1xuICAgIGJvcmRlcjogMDtcbiAgICBtYXJnaW46IDA7XG4gICAgLyogTW92ZSB0ZXh0YXJlYSBvdXQgb2YgdGhlIHNjcmVlbiB0byB0aGUgZmFyIGxlZnQsIHNvIHRoYXQgdGhlIGN1cnNvciBpcyBub3QgdmlzaWJsZSAqL1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICBvcGFjaXR5OiAwO1xuICAgIGxlZnQ6IC05OTk5ZW07XG4gICAgdG9wOiAwO1xuICAgIHdpZHRoOiAwO1xuICAgIGhlaWdodDogMDtcbiAgICB6LWluZGV4OiAtNTtcbiAgICAvKiogUHJldmVudCB3cmFwcGluZyBzbyB0aGUgSU1FIGFwcGVhcnMgYWdhaW5zdCB0aGUgdGV4dGFyZWEgYXQgdGhlIGNvcnJlY3QgcG9zaXRpb24gKi9cbiAgICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG4gICAgcmVzaXplOiBub25lO1xufVxuXG4ueHRlcm0gLmNvbXBvc2l0aW9uLXZpZXcge1xuICAgIC8qIFRPRE86IENvbXBvc2l0aW9uIHBvc2l0aW9uIGdvdCBtZXNzZWQgdXAgc29tZXdoZXJlICovXG4gICAgYmFja2dyb3VuZDogIzAwMDtcbiAgICBjb2xvcjogI0ZGRjtcbiAgICBkaXNwbGF5OiBub25lO1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICB3aGl0ZS1zcGFjZTogbm93cmFwO1xuICAgIHotaW5kZXg6IDE7XG59XG5cbi54dGVybSAuY29tcG9zaXRpb24tdmlldy5hY3RpdmUge1xuICAgIGRpc3BsYXk6IGJsb2NrO1xufVxuXG4ueHRlcm0gLnh0ZXJtLXZpZXdwb3J0IHtcbiAgICAvKiBPbiBPUyBYIHRoaXMgaXMgcmVxdWlyZWQgaW4gb3JkZXIgZm9yIHRoZSBzY3JvbGwgYmFyIHRvIGFwcGVhciBmdWxseSBvcGFxdWUgKi9cbiAgICBiYWNrZ3JvdW5kLWNvbG9yOiAjMDAwO1xuICAgIG92ZXJmbG93LXk6IHNjcm9sbDtcbiAgICBjdXJzb3I6IGRlZmF1bHQ7XG4gICAgcG9zaXRpb246IGFic29sdXRlO1xuICAgIHJpZ2h0OiAwO1xuICAgIGxlZnQ6IDA7XG4gICAgdG9wOiAwO1xuICAgIGJvdHRvbTogMDtcbn1cblxuLnh0ZXJtIC54dGVybS1zY3JlZW4ge1xuICAgIHBvc2l0aW9uOiByZWxhdGl2ZTtcbn1cblxuLnh0ZXJtIC54dGVybS1zY3JlZW4gY2FudmFzIHtcbiAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gICAgbGVmdDogMDtcbiAgICB0b3A6IDA7XG59XG5cbi54dGVybSAueHRlcm0tc2Nyb2xsLWFyZWEge1xuICAgIHZpc2liaWxpdHk6IGhpZGRlbjtcbn1cblxuLnh0ZXJtLWNoYXItbWVhc3VyZS1lbGVtZW50IHtcbiAgICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gICAgdmlzaWJpbGl0eTogaGlkZGVuO1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICB0b3A6IDA7XG4gICAgbGVmdDogLTk5OTllbTtcbiAgICBsaW5lLWhlaWdodDogbm9ybWFsO1xufVxuXG4ueHRlcm0ge1xuICAgIGN1cnNvcjogdGV4dDtcbn1cblxuLnh0ZXJtLmVuYWJsZS1tb3VzZS1ldmVudHMge1xuICAgIC8qIFdoZW4gbW91c2UgZXZlbnRzIGFyZSBlbmFibGVkIChlZy4gdG11eCksIHJldmVydCB0byB0aGUgc3RhbmRhcmQgcG9pbnRlciBjdXJzb3IgKi9cbiAgICBjdXJzb3I6IGRlZmF1bHQ7XG59XG5cbi54dGVybS54dGVybS1jdXJzb3ItcG9pbnRlcixcbi54dGVybSAueHRlcm0tY3Vyc29yLXBvaW50ZXIge1xuICAgIGN1cnNvcjogcG9pbnRlcjtcbn1cblxuLnh0ZXJtLmNvbHVtbi1zZWxlY3QuZm9jdXMge1xuICAgIC8qIENvbHVtbiBzZWxlY3Rpb24gbW9kZSAqL1xuICAgIGN1cnNvcjogY3Jvc3NoYWlyO1xufVxuXG4ueHRlcm0gLnh0ZXJtLWFjY2Vzc2liaWxpdHksXG4ueHRlcm0gLnh0ZXJtLW1lc3NhZ2Uge1xuICAgIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgICBsZWZ0OiAwO1xuICAgIHRvcDogMDtcbiAgICBib3R0b206IDA7XG4gICAgcmlnaHQ6IDA7XG4gICAgei1pbmRleDogMTA7XG4gICAgY29sb3I6IHRyYW5zcGFyZW50O1xufVxuXG4ueHRlcm0gLmxpdmUtcmVnaW9uIHtcbiAgICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gICAgbGVmdDogLTk5OTlweDtcbiAgICB3aWR0aDogMXB4O1xuICAgIGhlaWdodDogMXB4O1xuICAgIG92ZXJmbG93OiBoaWRkZW47XG59XG5cbi54dGVybS1kaW0ge1xuICAgIG9wYWNpdHk6IDAuNTtcbn1cblxuLnh0ZXJtLXVuZGVybGluZSB7XG4gICAgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7XG59XG5cbi54dGVybS1zdHJpa2V0aHJvdWdoIHtcbiAgICB0ZXh0LWRlY29yYXRpb246IGxpbmUtdGhyb3VnaDtcbn1cbicsIiJdKTtjb25zdCBhPXN9LDY0NTplPT57InVzZSBzdHJpY3QiO2UuZXhwb3J0cz1mdW5jdGlvbihlKXt2YXIgdD1bXTtyZXR1cm4gdC50b1N0cmluZz1mdW5jdGlvbigpe3JldHVybiB0aGlzLm1hcCgoZnVuY3Rpb24odCl7dmFyIHI9IiIsaT12b2lkIDAhPT10WzVdO3JldHVybiB0WzRdJiYocis9IkBzdXBwb3J0cyAoIi5jb25jYXQodFs0XSwiKSB7IikpLHRbMl0mJihyKz0iQG1lZGlhICIuY29uY2F0KHRbMl0sIiB7IikpLGkmJihyKz0iQGxheWVyIi5jb25jYXQodFs1XS5sZW5ndGg+MD8iICIuY29uY2F0KHRbNV0pOiIiLCIgeyIpKSxyKz1lKHQpLGkmJihyKz0ifSIpLHRbMl0mJihyKz0ifSIpLHRbNF0mJihyKz0ifSIpLHJ9KSkuam9pbigiIil9LHQuaT1mdW5jdGlvbihlLHIsaSxuLG8peyJzdHJpbmciPT10eXBlb2YgZSYmKGU9W1tudWxsLGUsdm9pZCAwXV0pO3ZhciBzPXt9O2lmKGkpZm9yKHZhciBhPTA7YTx0aGlzLmxlbmd0aDthKyspe3ZhciBjPXRoaXNbYV1bMF07bnVsbCE9YyYmKHNbY109ITApfWZvcih2YXIgbD0wO2w8ZS5sZW5ndGg7bCsrKXt2YXIgdT1bXS5jb25jYXQoZVtsXSk7aSYmc1t1WzBdXXx8KHZvaWQgMCE9PW8mJih2b2lkIDA9PT11WzVdfHwodVsxXT0iQGxheWVyIi5jb25jYXQodVs1XS5sZW5ndGg+MD8iICIuY29uY2F0KHVbNV0pOiIiLCIgeyIpLmNvbmNhdCh1WzFdLCJ9IikpLHVbNV09byksciYmKHVbMl0/KHVbMV09IkBtZWRpYSAiLmNvbmNhdCh1WzJdLCIgeyIpLmNvbmNhdCh1WzFdLCJ9IiksdVsyXT1yKTp1WzJdPXIpLG4mJih1WzRdPyh1WzFdPSJAc3VwcG9ydHMgKCIuY29uY2F0KHVbNF0sIikgeyIpLmNvbmNhdCh1WzFdLCJ9IiksdVs0XT1uKTp1WzRdPSIiLmNvbmNhdChuKSksdC5wdXNoKHUpKX19LHR9fSw4MTplPT57InVzZSBzdHJpY3QiO2UuZXhwb3J0cz1mdW5jdGlvbihlKXtyZXR1cm4gZVsxXX19LDQ4NjpmdW5jdGlvbihlLHQscil7dmFyIGk7ZT1yLm5tZChlKSxmdW5jdGlvbigpe3ZhciBuLG89IkV4cGVjdGVkIGEgZnVuY3Rpb24iLHM9Il9fbG9kYXNoX2hhc2hfdW5kZWZpbmVkX18iLGE9Il9fbG9kYXNoX3BsYWNlaG9sZGVyX18iLGM9MzIsbD0xMjgsdT0xLzAsaD05MDA3MTk5MjU0NzQwOTkxLGY9TmFOLF89NDI5NDk2NzI5NSxkPVtbImFyeSIsbF0sWyJiaW5kIiwxXSxbImJpbmRLZXkiLDJdLFsiY3VycnkiLDhdLFsiY3VycnlSaWdodCIsMTZdLFsiZmxpcCIsNTEyXSxbInBhcnRpYWwiLGNdLFsicGFydGlhbFJpZ2h0Iiw2NF0sWyJyZWFyZyIsMjU2XV0scD0iW29iamVjdCBBcmd1bWVudHNdIix2PSJbb2JqZWN0IEFycmF5XSIsZz0iW29iamVjdCBCb29sZWFuXSIseT0iW29iamVjdCBEYXRlXSIsbT0iW29iamVjdCBFcnJvcl0iLGI9IltvYmplY3QgRnVuY3Rpb25dIixTPSJbb2JqZWN0IEdlbmVyYXRvckZ1bmN0aW9uXSIsQz0iW29iamVjdCBNYXBdIix3PSJbb2JqZWN0IE51bWJlcl0iLEw9IltvYmplY3QgT2JqZWN0XSIsRT0iW29iamVjdCBQcm9taXNlXSIseD0iW29iamVjdCBSZWdFeHBdIixBPSJbb2JqZWN0IFNldF0iLGs9IltvYmplY3QgU3RyaW5nXSIsTT0iW29iamVjdCBTeW1ib2xdIixSPSJbb2JqZWN0IFdlYWtNYXBdIixUPSJbb2JqZWN0IEFycmF5QnVmZmVyXSIsTz0iW29iamVjdCBEYXRhVmlld10iLEI9IltvYmplY3QgRmxvYXQzMkFycmF5XSIsRD0iW29iamVjdCBGbG9hdDY0QXJyYXldIixQPSJbb2JqZWN0IEludDhBcnJheV0iLEk9IltvYmplY3QgSW50MTZBcnJheV0iLEg9IltvYmplY3QgSW50MzJBcnJheV0iLGo9IltvYmplY3QgVWludDhBcnJheV0iLEY9IltvYmplY3QgVWludDhDbGFtcGVkQXJyYXldIixXPSJbb2JqZWN0IFVpbnQxNkFycmF5XSIsVT0iW29iamVjdCBVaW50MzJBcnJheV0iLHE9L1xiX19wIFwrPSAnJzsvZyxOPS9cYihfX3AgXCs9KSAnJyBcKy9nLHo9LyhfX2VcKC4qP1wpfFxiX190XCkpIFwrXG4nJzsvZyxLPS8mKD86YW1wfGx0fGd0fHF1b3R8IzM5KTsvZyxWPS9bJjw+IiddL2csRz1SZWdFeHAoSy5zb3VyY2UpLFk9UmVnRXhwKFYuc291cmNlKSxYPS88JS0oW1xzXFNdKz8pJT4vZyxaPS88JShbXHNcU10rPyklPi9nLEo9LzwlPShbXHNcU10rPyklPi9nLCQ9L1wufFxbKD86W15bXF1dKnwoWyInXSkoPzooPyFcMSlbXlxcXXxcXC4pKj9cMSlcXS8sUT0vXlx3KiQvLGVlPS9bXi5bXF1dK3xcWyg/OigtP1xkKyg/OlwuXGQrKT8pfChbIiddKSgoPzooPyFcMilbXlxcXXxcXC4pKj8pXDIpXF18KD89KD86XC58XFtcXSkoPzpcLnxcW1xdfCQpKS9nLHRlPS9bXFxeJC4qKz8oKVtcXXt9fF0vZyxyZT1SZWdFeHAodGUuc291cmNlKSxpZT0vXlxzKy8sbmU9L1xzLyxvZT0vXHsoPzpcblwvXCogXFt3cmFwcGVkIHdpdGggLitcXSBcKlwvKT9cbj8vLHNlPS9ce1xuXC9cKiBcW3dyYXBwZWQgd2l0aCAoLispXF0gXCovLGFlPS8sPyAmIC8sY2U9L1teXHgwMC1ceDJmXHgzYS1ceDQwXHg1Yi1ceDYwXHg3Yi1ceDdmXSsvZyxsZT0vWygpPSx7fVxbXF1cL1xzXS8sdWU9L1xcKFxcKT8vZyxoZT0vXCRceyhbXlxcfV0qKD86XFwuW15cXH1dKikqKVx9L2csZmU9L1x3KiQvLF9lPS9eWy0rXTB4WzAtOWEtZl0rJC9pLGRlPS9eMGJbMDFdKyQvaSxwZT0vXlxbb2JqZWN0IC4rP0NvbnN0cnVjdG9yXF0kLyx2ZT0vXjBvWzAtN10rJC9pLGdlPS9eKD86MHxbMS05XVxkKikkLyx5ZT0vW1x4YzAtXHhkNlx4ZDgtXHhmNlx4ZjgtXHhmZlx1MDEwMC1cdTAxN2ZdL2csbWU9LygkXikvLGJlPS9bJ1xuXHJcdTIwMjhcdTIwMjlcXF0vZyxTZT0iXFx1MDMwMC1cXHUwMzZmXFx1ZmUyMC1cXHVmZTJmXFx1MjBkMC1cXHUyMGZmIixDZT0iYS16XFx4ZGYtXFx4ZjZcXHhmOC1cXHhmZiIsd2U9IkEtWlxceGMwLVxceGQ2XFx4ZDgtXFx4ZGUiLExlPSJcXHhhY1xceGIxXFx4ZDdcXHhmN1xceDAwLVxceDJmXFx4M2EtXFx4NDBcXHg1Yi1cXHg2MFxceDdiLVxceGJmXFx1MjAwMC1cXHUyMDZmIFxcdFxceDBiXFxmXFx4YTBcXHVmZWZmXFxuXFxyXFx1MjAyOFxcdTIwMjlcXHUxNjgwXFx1MTgwZVxcdTIwMDBcXHUyMDAxXFx1MjAwMlxcdTIwMDNcXHUyMDA0XFx1MjAwNVxcdTIwMDZcXHUyMDA3XFx1MjAwOFxcdTIwMDlcXHUyMDBhXFx1MjAyZlxcdTIwNWZcXHUzMDAwIixFZT0iWyIrTGUrIl0iLHhlPSJbIitTZSsiXSIsQWU9IlxcZCsiLGtlPSJbIitDZSsiXSIsTWU9IlteXFx1ZDgwMC1cXHVkZmZmIitMZStBZSsiXFx1MjcwMC1cXHUyN2JmIitDZSt3ZSsiXSIsUmU9IlxcdWQ4M2NbXFx1ZGZmYi1cXHVkZmZmXSIsVGU9IlteXFx1ZDgwMC1cXHVkZmZmXSIsT2U9Iig/OlxcdWQ4M2NbXFx1ZGRlNi1cXHVkZGZmXSl7Mn0iLEJlPSJbXFx1ZDgwMC1cXHVkYmZmXVtcXHVkYzAwLVxcdWRmZmZdIixEZT0iWyIrd2UrIl0iLFBlPSIoPzoiK2tlKyJ8IitNZSsiKSIsSWU9Iig/OiIrRGUrInwiK01lKyIpIixIZT0iKD86WyfigJldKD86ZHxsbHxtfHJlfHN8dHx2ZSkpPyIsamU9Iig/Olsn4oCZXSg/OkR8TEx8TXxSRXxTfFR8VkUpKT8iLEZlPSIoPzoiK3hlKyJ8IitSZSsiKT8iLFdlPSJbXFx1ZmUwZVxcdWZlMGZdPyIsVWU9V2UrRmUrIig/OlxcdTIwMGQoPzoiK1tUZSxPZSxCZV0uam9pbigifCIpKyIpIitXZStGZSsiKSoiLHFlPSIoPzoiK1siW1xcdTI3MDAtXFx1MjdiZl0iLE9lLEJlXS5qb2luKCJ8IikrIikiK1VlLE5lPSIoPzoiK1tUZSt4ZSsiPyIseGUsT2UsQmUsIltcXHVkODAwLVxcdWRmZmZdIl0uam9pbigifCIpKyIpIix6ZT1SZWdFeHAoIlsn4oCZXSIsImciKSxLZT1SZWdFeHAoeGUsImciKSxWZT1SZWdFeHAoUmUrIig/PSIrUmUrIil8IitOZStVZSwiZyIpLEdlPVJlZ0V4cChbRGUrIj8iK2tlKyIrIitIZSsiKD89IitbRWUsRGUsIiQiXS5qb2luKCJ8IikrIikiLEllKyIrIitqZSsiKD89IitbRWUsRGUrUGUsIiQiXS5qb2luKCJ8IikrIikiLERlKyI/IitQZSsiKyIrSGUsRGUrIisiK2plLCJcXGQqKD86MVNUfDJORHwzUkR8KD8hWzEyM10pXFxkVEgpKD89XFxifFthLXpfXSkiLCJcXGQqKD86MXN0fDJuZHwzcmR8KD8hWzEyM10pXFxkdGgpKD89XFxifFtBLVpfXSkiLEFlLHFlXS5qb2luKCJ8IiksImciKSxZZT1SZWdFeHAoIltcXHUyMDBkXFx1ZDgwMC1cXHVkZmZmIitTZSsiXFx1ZmUwZVxcdWZlMGZdIiksWGU9L1thLXpdW0EtWl18W0EtWl17Mn1bYS16XXxbMC05XVthLXpBLVpdfFthLXpBLVpdWzAtOV18W15hLXpBLVowLTkgXS8sWmU9WyJBcnJheSIsIkJ1ZmZlciIsIkRhdGFWaWV3IiwiRGF0ZSIsIkVycm9yIiwiRmxvYXQzMkFycmF5IiwiRmxvYXQ2NEFycmF5IiwiRnVuY3Rpb24iLCJJbnQ4QXJyYXkiLCJJbnQxNkFycmF5IiwiSW50MzJBcnJheSIsIk1hcCIsIk1hdGgiLCJPYmplY3QiLCJQcm9taXNlIiwiUmVnRXhwIiwiU2V0IiwiU3RyaW5nIiwiU3ltYm9sIiwiVHlwZUVycm9yIiwiVWludDhBcnJheSIsIlVpbnQ4Q2xhbXBlZEFycmF5IiwiVWludDE2QXJyYXkiLCJVaW50MzJBcnJheSIsIldlYWtNYXAiLCJfIiwiY2xlYXJUaW1lb3V0IiwiaXNGaW5pdGUiLCJwYXJzZUludCIsInNldFRpbWVvdXQiXSxKZT0tMSwkZT17fTskZVtCXT0kZVtEXT0kZVtQXT0kZVtJXT0kZVtIXT0kZVtqXT0kZVtGXT0kZVtXXT0kZVtVXT0hMCwkZVtwXT0kZVt2XT0kZVtUXT0kZVtnXT0kZVtPXT0kZVt5XT0kZVttXT0kZVtiXT0kZVtDXT0kZVt3XT0kZVtMXT0kZVt4XT0kZVtBXT0kZVtrXT0kZVtSXT0hMTt2YXIgUWU9e307UWVbcF09UWVbdl09UWVbVF09UWVbT109UWVbZ109UWVbeV09UWVbQl09UWVbRF09UWVbUF09UWVbSV09UWVbSF09UWVbQ109UWVbd109UWVbTF09UWVbeF09UWVbQV09UWVba109UWVbTV09UWVbal09UWVbRl09UWVbV109UWVbVV09ITAsUWVbbV09UWVbYl09UWVbUl09ITE7dmFyIGV0PXsiXFwiOiJcXCIsIiciOiInIiwiXG4iOiJuIiwiXHIiOiJyIiwiXHUyMDI4IjoidTIwMjgiLCJcdTIwMjkiOiJ1MjAyOSJ9LHR0PXBhcnNlRmxvYXQscnQ9cGFyc2VJbnQsaXQ9Im9iamVjdCI9PXR5cGVvZiByLmcmJnIuZyYmci5nLk9iamVjdD09PU9iamVjdCYmci5nLG50PSJvYmplY3QiPT10eXBlb2Ygc2VsZiYmc2VsZiYmc2VsZi5PYmplY3Q9PT1PYmplY3QmJnNlbGYsb3Q9aXR8fG50fHxGdW5jdGlvbigicmV0dXJuIHRoaXMiKSgpLHN0PXQmJiF0Lm5vZGVUeXBlJiZ0LGF0PXN0JiZlJiYhZS5ub2RlVHlwZSYmZSxjdD1hdCYmYXQuZXhwb3J0cz09PXN0LGx0PWN0JiZpdC5wcm9jZXNzLHV0PWZ1bmN0aW9uKCl7dHJ5e3JldHVybiBhdCYmYXQucmVxdWlyZSYmYXQucmVxdWlyZSgidXRpbCIpLnR5cGVzfHxsdCYmbHQuYmluZGluZyYmbHQuYmluZGluZygidXRpbCIpfWNhdGNoKGUpe319KCksaHQ9dXQmJnV0LmlzQXJyYXlCdWZmZXIsZnQ9dXQmJnV0LmlzRGF0ZSxfdD11dCYmdXQuaXNNYXAsZHQ9dXQmJnV0LmlzUmVnRXhwLHB0PXV0JiZ1dC5pc1NldCx2dD11dCYmdXQuaXNUeXBlZEFycmF5O2Z1bmN0aW9uIGd0KGUsdCxyKXtzd2l0Y2goci5sZW5ndGgpe2Nhc2UgMDpyZXR1cm4gZS5jYWxsKHQpO2Nhc2UgMTpyZXR1cm4gZS5jYWxsKHQsclswXSk7Y2FzZSAyOnJldHVybiBlLmNhbGwodCxyWzBdLHJbMV0pO2Nhc2UgMzpyZXR1cm4gZS5jYWxsKHQsclswXSxyWzFdLHJbMl0pfXJldHVybiBlLmFwcGx5KHQscil9ZnVuY3Rpb24geXQoZSx0LHIsaSl7Zm9yKHZhciBuPS0xLG89bnVsbD09ZT8wOmUubGVuZ3RoOysrbjxvOyl7dmFyIHM9ZVtuXTt0KGkscyxyKHMpLGUpfXJldHVybiBpfWZ1bmN0aW9uIG10KGUsdCl7Zm9yKHZhciByPS0xLGk9bnVsbD09ZT8wOmUubGVuZ3RoOysrcjxpJiYhMSE9PXQoZVtyXSxyLGUpOyk7cmV0dXJuIGV9ZnVuY3Rpb24gYnQoZSx0KXtmb3IodmFyIHI9bnVsbD09ZT8wOmUubGVuZ3RoO3ItLSYmITEhPT10KGVbcl0scixlKTspO3JldHVybiBlfWZ1bmN0aW9uIFN0KGUsdCl7Zm9yKHZhciByPS0xLGk9bnVsbD09ZT8wOmUubGVuZ3RoOysrcjxpOylpZighdChlW3JdLHIsZSkpcmV0dXJuITE7cmV0dXJuITB9ZnVuY3Rpb24gQ3QoZSx0KXtmb3IodmFyIHI9LTEsaT1udWxsPT1lPzA6ZS5sZW5ndGgsbj0wLG89W107KytyPGk7KXt2YXIgcz1lW3JdO3QocyxyLGUpJiYob1tuKytdPXMpfXJldHVybiBvfWZ1bmN0aW9uIHd0KGUsdCl7cmV0dXJuIShudWxsPT1lfHwhZS5sZW5ndGgpJiZCdChlLHQsMCk+LTF9ZnVuY3Rpb24gTHQoZSx0LHIpe2Zvcih2YXIgaT0tMSxuPW51bGw9PWU/MDplLmxlbmd0aDsrK2k8bjspaWYocih0LGVbaV0pKXJldHVybiEwO3JldHVybiExfWZ1bmN0aW9uIEV0KGUsdCl7Zm9yKHZhciByPS0xLGk9bnVsbD09ZT8wOmUubGVuZ3RoLG49QXJyYXkoaSk7KytyPGk7KW5bcl09dChlW3JdLHIsZSk7cmV0dXJuIG59ZnVuY3Rpb24geHQoZSx0KXtmb3IodmFyIHI9LTEsaT10Lmxlbmd0aCxuPWUubGVuZ3RoOysrcjxpOyllW24rcl09dFtyXTtyZXR1cm4gZX1mdW5jdGlvbiBBdChlLHQscixpKXt2YXIgbj0tMSxvPW51bGw9PWU/MDplLmxlbmd0aDtmb3IoaSYmbyYmKHI9ZVsrK25dKTsrK248bzspcj10KHIsZVtuXSxuLGUpO3JldHVybiByfWZ1bmN0aW9uIGt0KGUsdCxyLGkpe3ZhciBuPW51bGw9PWU/MDplLmxlbmd0aDtmb3IoaSYmbiYmKHI9ZVstLW5dKTtuLS07KXI9dChyLGVbbl0sbixlKTtyZXR1cm4gcn1mdW5jdGlvbiBNdChlLHQpe2Zvcih2YXIgcj0tMSxpPW51bGw9PWU/MDplLmxlbmd0aDsrK3I8aTspaWYodChlW3JdLHIsZSkpcmV0dXJuITA7cmV0dXJuITF9dmFyIFJ0PUh0KCJsZW5ndGgiKTtmdW5jdGlvbiBUdChlLHQscil7dmFyIGk7cmV0dXJuIHIoZSwoZnVuY3Rpb24oZSxyLG4pe2lmKHQoZSxyLG4pKXJldHVybiBpPXIsITF9KSksaX1mdW5jdGlvbiBPdChlLHQscixpKXtmb3IodmFyIG49ZS5sZW5ndGgsbz1yKyhpPzE6LTEpO2k/by0tOisrbzxuOylpZih0KGVbb10sbyxlKSlyZXR1cm4gbztyZXR1cm4tMX1mdW5jdGlvbiBCdChlLHQscil7cmV0dXJuIHQ9PXQ/ZnVuY3Rpb24oZSx0LHIpe2Zvcih2YXIgaT1yLTEsbj1lLmxlbmd0aDsrK2k8bjspaWYoZVtpXT09PXQpcmV0dXJuIGk7cmV0dXJuLTF9KGUsdCxyKTpPdChlLFB0LHIpfWZ1bmN0aW9uIER0KGUsdCxyLGkpe2Zvcih2YXIgbj1yLTEsbz1lLmxlbmd0aDsrK248bzspaWYoaShlW25dLHQpKXJldHVybiBuO3JldHVybi0xfWZ1bmN0aW9uIFB0KGUpe3JldHVybiBlIT1lfWZ1bmN0aW9uIEl0KGUsdCl7dmFyIHI9bnVsbD09ZT8wOmUubGVuZ3RoO3JldHVybiByP1d0KGUsdCkvcjpmfWZ1bmN0aW9uIEh0KGUpe3JldHVybiBmdW5jdGlvbih0KXtyZXR1cm4gbnVsbD09dD9uOnRbZV19fWZ1bmN0aW9uIGp0KGUpe3JldHVybiBmdW5jdGlvbih0KXtyZXR1cm4gbnVsbD09ZT9uOmVbdF19fWZ1bmN0aW9uIEZ0KGUsdCxyLGksbil7cmV0dXJuIG4oZSwoZnVuY3Rpb24oZSxuLG8pe3I9aT8oaT0hMSxlKTp0KHIsZSxuLG8pfSkpLHJ9ZnVuY3Rpb24gV3QoZSx0KXtmb3IodmFyIHIsaT0tMSxvPWUubGVuZ3RoOysraTxvOyl7dmFyIHM9dChlW2ldKTtzIT09biYmKHI9cj09PW4/czpyK3MpfXJldHVybiByfWZ1bmN0aW9uIFV0KGUsdCl7Zm9yKHZhciByPS0xLGk9QXJyYXkoZSk7KytyPGU7KWlbcl09dChyKTtyZXR1cm4gaX1mdW5jdGlvbiBxdChlKXtyZXR1cm4gZT9lLnNsaWNlKDAsc3IoZSkrMSkucmVwbGFjZShpZSwiIik6ZX1mdW5jdGlvbiBOdChlKXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIGUodCl9fWZ1bmN0aW9uIHp0KGUsdCl7cmV0dXJuIEV0KHQsKGZ1bmN0aW9uKHQpe3JldHVybiBlW3RdfSkpfWZ1bmN0aW9uIEt0KGUsdCl7cmV0dXJuIGUuaGFzKHQpfWZ1bmN0aW9uIFZ0KGUsdCl7Zm9yKHZhciByPS0xLGk9ZS5sZW5ndGg7KytyPGkmJkJ0KHQsZVtyXSwwKT4tMTspO3JldHVybiByfWZ1bmN0aW9uIEd0KGUsdCl7Zm9yKHZhciByPWUubGVuZ3RoO3ItLSYmQnQodCxlW3JdLDApPi0xOyk7cmV0dXJuIHJ9ZnVuY3Rpb24gWXQoZSx0KXtmb3IodmFyIHI9ZS5sZW5ndGgsaT0wO3ItLTspZVtyXT09PXQmJisraTtyZXR1cm4gaX12YXIgWHQ9anQoe8OAOiJBIizDgToiQSIsw4I6IkEiLMODOiJBIizDhDoiQSIsw4U6IkEiLMOgOiJhIizDoToiYSIsw6I6ImEiLMOjOiJhIizDpDoiYSIsw6U6ImEiLMOHOiJDIizDpzoiYyIsw5A6IkQiLMOwOiJkIizDiDoiRSIsw4k6IkUiLMOKOiJFIizDizoiRSIsw6g6ImUiLMOpOiJlIizDqjoiZSIsw6s6ImUiLMOMOiJJIizDjToiSSIsw446IkkiLMOPOiJJIizDrDoiaSIsw606ImkiLMOuOiJpIizDrzoiaSIsw5E6Ik4iLMOxOiJuIizDkjoiTyIsw5M6Ik8iLMOUOiJPIizDlToiTyIsw5Y6Ik8iLMOYOiJPIizDsjoibyIsw7M6Im8iLMO0OiJvIizDtToibyIsw7Y6Im8iLMO4OiJvIizDmToiVSIsw5o6IlUiLMObOiJVIizDnDoiVSIsw7k6InUiLMO6OiJ1IizDuzoidSIsw7w6InUiLMOdOiJZIizDvToieSIsw786InkiLMOGOiJBZSIsw6Y6ImFlIizDnjoiVGgiLMO+OiJ0aCIsw586InNzIizEgDoiQSIsxII6IkEiLMSEOiJBIizEgToiYSIsxIM6ImEiLMSFOiJhIizEhjoiQyIsxIg6IkMiLMSKOiJDIizEjDoiQyIsxIc6ImMiLMSJOiJjIizEizoiYyIsxI06ImMiLMSOOiJEIizEkDoiRCIsxI86ImQiLMSROiJkIizEkjoiRSIsxJQ6IkUiLMSWOiJFIizEmDoiRSIsxJo6IkUiLMSTOiJlIizElToiZSIsxJc6ImUiLMSZOiJlIizEmzoiZSIsxJw6IkciLMSeOiJHIizEoDoiRyIsxKI6IkciLMSdOiJnIizEnzoiZyIsxKE6ImciLMSjOiJnIizEpDoiSCIsxKY6IkgiLMSlOiJoIizEpzoiaCIsxKg6IkkiLMSqOiJJIizErDoiSSIsxK46IkkiLMSwOiJJIizEqToiaSIsxKs6ImkiLMStOiJpIizErzoiaSIsxLE6ImkiLMS0OiJKIizEtToiaiIsxLY6IksiLMS3OiJrIizEuDoiayIsxLk6IkwiLMS7OiJMIizEvToiTCIsxL86IkwiLMWBOiJMIizEujoibCIsxLw6ImwiLMS+OiJsIizFgDoibCIsxYI6ImwiLMWDOiJOIizFhToiTiIsxYc6Ik4iLMWKOiJOIizFhDoibiIsxYY6Im4iLMWIOiJuIizFizoibiIsxYw6Ik8iLMWOOiJPIizFkDoiTyIsxY06Im8iLMWPOiJvIizFkToibyIsxZQ6IlIiLMWWOiJSIizFmDoiUiIsxZU6InIiLMWXOiJyIizFmToiciIsxZo6IlMiLMWcOiJTIizFnjoiUyIsxaA6IlMiLMWbOiJzIizFnToicyIsxZ86InMiLMWhOiJzIizFojoiVCIsxaQ6IlQiLMWmOiJUIizFozoidCIsxaU6InQiLMWnOiJ0IizFqDoiVSIsxao6IlUiLMWsOiJVIizFrjoiVSIsxbA6IlUiLMWyOiJVIizFqToidSIsxas6InUiLMWtOiJ1IizFrzoidSIsxbE6InUiLMWzOiJ1IizFtDoiVyIsxbU6InciLMW2OiJZIizFtzoieSIsxbg6IlkiLMW5OiJaIizFuzoiWiIsxb06IloiLMW6OiJ6IizFvDoieiIsxb46InoiLMSyOiJJSiIsxLM6ImlqIizFkjoiT2UiLMWTOiJvZSIsxYk6IiduIizFvzoicyJ9KSxadD1qdCh7IiYiOiImYW1wOyIsIjwiOiImbHQ7IiwiPiI6IiZndDsiLCciJzoiJnF1b3Q7IiwiJyI6IiYjMzk7In0pO2Z1bmN0aW9uIEp0KGUpe3JldHVybiJcXCIrZXRbZV19ZnVuY3Rpb24gJHQoZSl7cmV0dXJuIFllLnRlc3QoZSl9ZnVuY3Rpb24gUXQoZSl7dmFyIHQ9LTEscj1BcnJheShlLnNpemUpO3JldHVybiBlLmZvckVhY2goKGZ1bmN0aW9uKGUsaSl7clsrK3RdPVtpLGVdfSkpLHJ9ZnVuY3Rpb24gZXIoZSx0KXtyZXR1cm4gZnVuY3Rpb24ocil7cmV0dXJuIGUodChyKSl9fWZ1bmN0aW9uIHRyKGUsdCl7Zm9yKHZhciByPS0xLGk9ZS5sZW5ndGgsbj0wLG89W107KytyPGk7KXt2YXIgcz1lW3JdO3MhPT10JiZzIT09YXx8KGVbcl09YSxvW24rK109cil9cmV0dXJuIG99ZnVuY3Rpb24gcnIoZSl7dmFyIHQ9LTEscj1BcnJheShlLnNpemUpO3JldHVybiBlLmZvckVhY2goKGZ1bmN0aW9uKGUpe3JbKyt0XT1lfSkpLHJ9ZnVuY3Rpb24gaXIoZSl7dmFyIHQ9LTEscj1BcnJheShlLnNpemUpO3JldHVybiBlLmZvckVhY2goKGZ1bmN0aW9uKGUpe3JbKyt0XT1bZSxlXX0pKSxyfWZ1bmN0aW9uIG5yKGUpe3JldHVybiAkdChlKT9mdW5jdGlvbihlKXtmb3IodmFyIHQ9VmUubGFzdEluZGV4PTA7VmUudGVzdChlKTspKyt0O3JldHVybiB0fShlKTpSdChlKX1mdW5jdGlvbiBvcihlKXtyZXR1cm4gJHQoZSk/ZnVuY3Rpb24oZSl7cmV0dXJuIGUubWF0Y2goVmUpfHxbXX0oZSk6ZnVuY3Rpb24oZSl7cmV0dXJuIGUuc3BsaXQoIiIpfShlKX1mdW5jdGlvbiBzcihlKXtmb3IodmFyIHQ9ZS5sZW5ndGg7dC0tJiZuZS50ZXN0KGUuY2hhckF0KHQpKTspO3JldHVybiB0fXZhciBhcj1qdCh7IiZhbXA7IjoiJiIsIiZsdDsiOiI8IiwiJmd0OyI6Ij4iLCImcXVvdDsiOiciJywiJiMzOTsiOiInIn0pLGNyPWZ1bmN0aW9uIGUodCl7dmFyIHIsaT0odD1udWxsPT10P290OmNyLmRlZmF1bHRzKG90Lk9iamVjdCgpLHQsY3IucGljayhvdCxaZSkpKS5BcnJheSxuZT10LkRhdGUsU2U9dC5FcnJvcixDZT10LkZ1bmN0aW9uLHdlPXQuTWF0aCxMZT10Lk9iamVjdCxFZT10LlJlZ0V4cCx4ZT10LlN0cmluZyxBZT10LlR5cGVFcnJvcixrZT1pLnByb3RvdHlwZSxNZT1DZS5wcm90b3R5cGUsUmU9TGUucHJvdG90eXBlLFRlPXRbIl9fY29yZS1qc19zaGFyZWRfXyJdLE9lPU1lLnRvU3RyaW5nLEJlPVJlLmhhc093blByb3BlcnR5LERlPTAsUGU9KHI9L1teLl0rJC8uZXhlYyhUZSYmVGUua2V5cyYmVGUua2V5cy5JRV9QUk9UT3x8IiIpKT8iU3ltYm9sKHNyYylfMS4iK3I6IiIsSWU9UmUudG9TdHJpbmcsSGU9T2UuY2FsbChMZSksamU9b3QuXyxGZT1FZSgiXiIrT2UuY2FsbChCZSkucmVwbGFjZSh0ZSwiXFwkJiIpLnJlcGxhY2UoL2hhc093blByb3BlcnR5fChmdW5jdGlvbikuKj8oPz1cXFwoKXwgZm9yIC4rPyg/PVxcXF0pL2csIiQxLio/IikrIiQiKSxXZT1jdD90LkJ1ZmZlcjpuLFVlPXQuU3ltYm9sLHFlPXQuVWludDhBcnJheSxOZT1XZT9XZS5hbGxvY1Vuc2FmZTpuLFZlPWVyKExlLmdldFByb3RvdHlwZU9mLExlKSxZZT1MZS5jcmVhdGUsZXQ9UmUucHJvcGVydHlJc0VudW1lcmFibGUsaXQ9a2Uuc3BsaWNlLG50PVVlP1VlLmlzQ29uY2F0U3ByZWFkYWJsZTpuLHN0PVVlP1VlLml0ZXJhdG9yOm4sYXQ9VWU/VWUudG9TdHJpbmdUYWc6bixsdD1mdW5jdGlvbigpe3RyeXt2YXIgZT1sbyhMZSwiZGVmaW5lUHJvcGVydHkiKTtyZXR1cm4gZSh7fSwiIix7fSksZX1jYXRjaChlKXt9fSgpLHV0PXQuY2xlYXJUaW1lb3V0IT09b3QuY2xlYXJUaW1lb3V0JiZ0LmNsZWFyVGltZW91dCxSdD1uZSYmbmUubm93IT09b3QuRGF0ZS5ub3cmJm5lLm5vdyxqdD10LnNldFRpbWVvdXQhPT1vdC5zZXRUaW1lb3V0JiZ0LnNldFRpbWVvdXQsbHI9d2UuY2VpbCx1cj13ZS5mbG9vcixocj1MZS5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMsZnI9V2U/V2UuaXNCdWZmZXI6bixfcj10LmlzRmluaXRlLGRyPWtlLmpvaW4scHI9ZXIoTGUua2V5cyxMZSksdnI9d2UubWF4LGdyPXdlLm1pbix5cj1uZS5ub3csbXI9dC5wYXJzZUludCxicj13ZS5yYW5kb20sU3I9a2UucmV2ZXJzZSxDcj1sbyh0LCJEYXRhVmlldyIpLHdyPWxvKHQsIk1hcCIpLExyPWxvKHQsIlByb21pc2UiKSxFcj1sbyh0LCJTZXQiKSx4cj1sbyh0LCJXZWFrTWFwIiksQXI9bG8oTGUsImNyZWF0ZSIpLGtyPXhyJiZuZXcgeHIsTXI9e30sUnI9Rm8oQ3IpLFRyPUZvKHdyKSxPcj1GbyhMciksQnI9Rm8oRXIpLERyPUZvKHhyKSxQcj1VZT9VZS5wcm90b3R5cGU6bixJcj1Qcj9Qci52YWx1ZU9mOm4sSHI9UHI/UHIudG9TdHJpbmc6bjtmdW5jdGlvbiBqcihlKXtpZihyYShlKSYmIUtzKGUpJiYhKGUgaW5zdGFuY2VvZiBxcikpe2lmKGUgaW5zdGFuY2VvZiBVcilyZXR1cm4gZTtpZihCZS5jYWxsKGUsIl9fd3JhcHBlZF9fIikpcmV0dXJuIFdvKGUpfXJldHVybiBuZXcgVXIoZSl9dmFyIEZyPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe31yZXR1cm4gZnVuY3Rpb24odCl7aWYoIXRhKHQpKXJldHVybnt9O2lmKFllKXJldHVybiBZZSh0KTtlLnByb3RvdHlwZT10O3ZhciByPW5ldyBlO3JldHVybiBlLnByb3RvdHlwZT1uLHJ9fSgpO2Z1bmN0aW9uIFdyKCl7fWZ1bmN0aW9uIFVyKGUsdCl7dGhpcy5fX3dyYXBwZWRfXz1lLHRoaXMuX19hY3Rpb25zX189W10sdGhpcy5fX2NoYWluX189ISF0LHRoaXMuX19pbmRleF9fPTAsdGhpcy5fX3ZhbHVlc19fPW59ZnVuY3Rpb24gcXIoZSl7dGhpcy5fX3dyYXBwZWRfXz1lLHRoaXMuX19hY3Rpb25zX189W10sdGhpcy5fX2Rpcl9fPTEsdGhpcy5fX2ZpbHRlcmVkX189ITEsdGhpcy5fX2l0ZXJhdGVlc19fPVtdLHRoaXMuX190YWtlQ291bnRfXz1fLHRoaXMuX192aWV3c19fPVtdfWZ1bmN0aW9uIE5yKGUpe3ZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoO2Zvcih0aGlzLmNsZWFyKCk7Kyt0PHI7KXt2YXIgaT1lW3RdO3RoaXMuc2V0KGlbMF0saVsxXSl9fWZ1bmN0aW9uIHpyKGUpe3ZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoO2Zvcih0aGlzLmNsZWFyKCk7Kyt0PHI7KXt2YXIgaT1lW3RdO3RoaXMuc2V0KGlbMF0saVsxXSl9fWZ1bmN0aW9uIEtyKGUpe3ZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoO2Zvcih0aGlzLmNsZWFyKCk7Kyt0PHI7KXt2YXIgaT1lW3RdO3RoaXMuc2V0KGlbMF0saVsxXSl9fWZ1bmN0aW9uIFZyKGUpe3ZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoO2Zvcih0aGlzLl9fZGF0YV9fPW5ldyBLcjsrK3Q8cjspdGhpcy5hZGQoZVt0XSl9ZnVuY3Rpb24gR3IoZSl7dmFyIHQ9dGhpcy5fX2RhdGFfXz1uZXcgenIoZSk7dGhpcy5zaXplPXQuc2l6ZX1mdW5jdGlvbiBZcihlLHQpe3ZhciByPUtzKGUpLGk9IXImJnpzKGUpLG49IXImJiFpJiZYcyhlKSxvPSFyJiYhaSYmIW4mJnVhKGUpLHM9cnx8aXx8bnx8byxhPXM/VXQoZS5sZW5ndGgseGUpOltdLGM9YS5sZW5ndGg7Zm9yKHZhciBsIGluIGUpIXQmJiFCZS5jYWxsKGUsbCl8fHMmJigibGVuZ3RoIj09bHx8biYmKCJvZmZzZXQiPT1sfHwicGFyZW50Ij09bCl8fG8mJigiYnVmZmVyIj09bHx8ImJ5dGVMZW5ndGgiPT1sfHwiYnl0ZU9mZnNldCI9PWwpfHxnbyhsLGMpKXx8YS5wdXNoKGwpO3JldHVybiBhfWZ1bmN0aW9uIFhyKGUpe3ZhciB0PWUubGVuZ3RoO3JldHVybiB0P2VbS2koMCx0LTEpXTpufWZ1bmN0aW9uIFpyKGUsdCl7cmV0dXJuIERvKEFuKGUpLG9pKHQsMCxlLmxlbmd0aCkpfWZ1bmN0aW9uIEpyKGUpe3JldHVybiBEbyhBbihlKSl9ZnVuY3Rpb24gJHIoZSx0LHIpeyhyIT09biYmIVVzKGVbdF0scil8fHI9PT1uJiYhKHQgaW4gZSkpJiZpaShlLHQscil9ZnVuY3Rpb24gUXIoZSx0LHIpe3ZhciBpPWVbdF07QmUuY2FsbChlLHQpJiZVcyhpLHIpJiYociE9PW58fHQgaW4gZSl8fGlpKGUsdCxyKX1mdW5jdGlvbiBlaShlLHQpe2Zvcih2YXIgcj1lLmxlbmd0aDtyLS07KWlmKFVzKGVbcl1bMF0sdCkpcmV0dXJuIHI7cmV0dXJuLTF9ZnVuY3Rpb24gdGkoZSx0LHIsaSl7cmV0dXJuIHVpKGUsKGZ1bmN0aW9uKGUsbixvKXt0KGksZSxyKGUpLG8pfSkpLGl9ZnVuY3Rpb24gcmkoZSx0KXtyZXR1cm4gZSYma24odCxPYSh0KSxlKX1mdW5jdGlvbiBpaShlLHQscil7Il9fcHJvdG9fXyI9PXQmJmx0P2x0KGUsdCx7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITAsdmFsdWU6cix3cml0YWJsZTohMH0pOmVbdF09cn1mdW5jdGlvbiBuaShlLHQpe2Zvcih2YXIgcj0tMSxvPXQubGVuZ3RoLHM9aShvKSxhPW51bGw9PWU7KytyPG87KXNbcl09YT9uOkFhKGUsdFtyXSk7cmV0dXJuIHN9ZnVuY3Rpb24gb2koZSx0LHIpe3JldHVybiBlPT1lJiYociE9PW4mJihlPWU8PXI/ZTpyKSx0IT09biYmKGU9ZT49dD9lOnQpKSxlfWZ1bmN0aW9uIHNpKGUsdCxyLGksbyxzKXt2YXIgYSxjPTEmdCxsPTImdCx1PTQmdDtpZihyJiYoYT1vP3IoZSxpLG8scyk6cihlKSksYSE9PW4pcmV0dXJuIGE7aWYoIXRhKGUpKXJldHVybiBlO3ZhciBoPUtzKGUpO2lmKGgpe2lmKGE9ZnVuY3Rpb24oZSl7dmFyIHQ9ZS5sZW5ndGgscj1uZXcgZS5jb25zdHJ1Y3Rvcih0KTtyZXR1cm4gdCYmInN0cmluZyI9PXR5cGVvZiBlWzBdJiZCZS5jYWxsKGUsImluZGV4IikmJihyLmluZGV4PWUuaW5kZXgsci5pbnB1dD1lLmlucHV0KSxyfShlKSwhYylyZXR1cm4gQW4oZSxhKX1lbHNle3ZhciBmPWZvKGUpLF89Zj09Ynx8Zj09UztpZihYcyhlKSlyZXR1cm4gU24oZSxjKTtpZihmPT1MfHxmPT1wfHxfJiYhbyl7aWYoYT1sfHxfP3t9OnBvKGUpLCFjKXJldHVybiBsP2Z1bmN0aW9uKGUsdCl7cmV0dXJuIGtuKGUsaG8oZSksdCl9KGUsZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYma24odCxCYSh0KSxlKX0oYSxlKSk6ZnVuY3Rpb24oZSx0KXtyZXR1cm4ga24oZSx1byhlKSx0KX0oZSxyaShhLGUpKX1lbHNle2lmKCFRZVtmXSlyZXR1cm4gbz9lOnt9O2E9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49ZS5jb25zdHJ1Y3Rvcjtzd2l0Y2godCl7Y2FzZSBUOnJldHVybiBDbihlKTtjYXNlIGc6Y2FzZSB5OnJldHVybiBuZXcgbigrZSk7Y2FzZSBPOnJldHVybiBmdW5jdGlvbihlLHQpe3ZhciByPXQ/Q24oZS5idWZmZXIpOmUuYnVmZmVyO3JldHVybiBuZXcgZS5jb25zdHJ1Y3RvcihyLGUuYnl0ZU9mZnNldCxlLmJ5dGVMZW5ndGgpfShlLHIpO2Nhc2UgQjpjYXNlIEQ6Y2FzZSBQOmNhc2UgSTpjYXNlIEg6Y2FzZSBqOmNhc2UgRjpjYXNlIFc6Y2FzZSBVOnJldHVybiB3bihlLHIpO2Nhc2UgQzpyZXR1cm4gbmV3IG47Y2FzZSB3OmNhc2UgazpyZXR1cm4gbmV3IG4oZSk7Y2FzZSB4OnJldHVybiBmdW5jdGlvbihlKXt2YXIgdD1uZXcgZS5jb25zdHJ1Y3RvcihlLnNvdXJjZSxmZS5leGVjKGUpKTtyZXR1cm4gdC5sYXN0SW5kZXg9ZS5sYXN0SW5kZXgsdH0oZSk7Y2FzZSBBOnJldHVybiBuZXcgbjtjYXNlIE06cmV0dXJuIGk9ZSxJcj9MZShJci5jYWxsKGkpKTp7fX19KGUsZixjKX19c3x8KHM9bmV3IEdyKTt2YXIgZD1zLmdldChlKTtpZihkKXJldHVybiBkO3Muc2V0KGUsYSksYWEoZSk/ZS5mb3JFYWNoKChmdW5jdGlvbihpKXthLmFkZChzaShpLHQscixpLGUscykpfSkpOmlhKGUpJiZlLmZvckVhY2goKGZ1bmN0aW9uKGksbil7YS5zZXQobixzaShpLHQscixuLGUscykpfSkpO3ZhciB2PWg/bjoodT9sP3JvOnRvOmw/QmE6T2EpKGUpO3JldHVybiBtdCh2fHxlLChmdW5jdGlvbihpLG4pe3YmJihpPWVbbj1pXSksUXIoYSxuLHNpKGksdCxyLG4sZSxzKSl9KSksYX1mdW5jdGlvbiBhaShlLHQscil7dmFyIGk9ci5sZW5ndGg7aWYobnVsbD09ZSlyZXR1cm4haTtmb3IoZT1MZShlKTtpLS07KXt2YXIgbz1yW2ldLHM9dFtvXSxhPWVbb107aWYoYT09PW4mJiEobyBpbiBlKXx8IXMoYSkpcmV0dXJuITF9cmV0dXJuITB9ZnVuY3Rpb24gY2koZSx0LHIpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiBlKXRocm93IG5ldyBBZShvKTtyZXR1cm4gUm8oKGZ1bmN0aW9uKCl7ZS5hcHBseShuLHIpfSksdCl9ZnVuY3Rpb24gbGkoZSx0LHIsaSl7dmFyIG49LTEsbz13dCxzPSEwLGE9ZS5sZW5ndGgsYz1bXSxsPXQubGVuZ3RoO2lmKCFhKXJldHVybiBjO3ImJih0PUV0KHQsTnQocikpKSxpPyhvPUx0LHM9ITEpOnQubGVuZ3RoPj0yMDAmJihvPUt0LHM9ITEsdD1uZXcgVnIodCkpO2U6Zm9yKDsrK248YTspe3ZhciB1PWVbbl0saD1udWxsPT1yP3U6cih1KTtpZih1PWl8fDAhPT11P3U6MCxzJiZoPT1oKXtmb3IodmFyIGY9bDtmLS07KWlmKHRbZl09PT1oKWNvbnRpbnVlIGU7Yy5wdXNoKHUpfWVsc2Ugbyh0LGgsaSl8fGMucHVzaCh1KX1yZXR1cm4gY31qci50ZW1wbGF0ZVNldHRpbmdzPXtlc2NhcGU6WCxldmFsdWF0ZTpaLGludGVycG9sYXRlOkosdmFyaWFibGU6IiIsaW1wb3J0czp7Xzpqcn19LGpyLnByb3RvdHlwZT1Xci5wcm90b3R5cGUsanIucHJvdG90eXBlLmNvbnN0cnVjdG9yPWpyLFVyLnByb3RvdHlwZT1GcihXci5wcm90b3R5cGUpLFVyLnByb3RvdHlwZS5jb25zdHJ1Y3Rvcj1Vcixxci5wcm90b3R5cGU9RnIoV3IucHJvdG90eXBlKSxxci5wcm90b3R5cGUuY29uc3RydWN0b3I9cXIsTnIucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7dGhpcy5fX2RhdGFfXz1Bcj9BcihudWxsKTp7fSx0aGlzLnNpemU9MH0sTnIucHJvdG90eXBlLmRlbGV0ZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzLmhhcyhlKSYmZGVsZXRlIHRoaXMuX19kYXRhX19bZV07cmV0dXJuIHRoaXMuc2l6ZS09dD8xOjAsdH0sTnIucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9fZGF0YV9fO2lmKEFyKXt2YXIgcj10W2VdO3JldHVybiByPT09cz9uOnJ9cmV0dXJuIEJlLmNhbGwodCxlKT90W2VdOm59LE5yLnByb3RvdHlwZS5oYXM9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fX2RhdGFfXztyZXR1cm4gQXI/dFtlXSE9PW46QmUuY2FsbCh0LGUpfSxOci5wcm90b3R5cGUuc2V0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5fX2RhdGFfXztyZXR1cm4gdGhpcy5zaXplKz10aGlzLmhhcyhlKT8wOjEscltlXT1BciYmdD09PW4/czp0LHRoaXN9LHpyLnByb3RvdHlwZS5jbGVhcj1mdW5jdGlvbigpe3RoaXMuX19kYXRhX189W10sdGhpcy5zaXplPTB9LHpyLnByb3RvdHlwZS5kZWxldGU9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fX2RhdGFfXyxyPWVpKHQsZSk7cmV0dXJuIShyPDB8fChyPT10Lmxlbmd0aC0xP3QucG9wKCk6aXQuY2FsbCh0LHIsMSksLS10aGlzLnNpemUsMCkpfSx6ci5wcm90b3R5cGUuZ2V0PWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMuX19kYXRhX18scj1laSh0LGUpO3JldHVybiByPDA/bjp0W3JdWzFdfSx6ci5wcm90b3R5cGUuaGFzPWZ1bmN0aW9uKGUpe3JldHVybiBlaSh0aGlzLl9fZGF0YV9fLGUpPi0xfSx6ci5wcm90b3R5cGUuc2V0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5fX2RhdGFfXyxpPWVpKHIsZSk7cmV0dXJuIGk8MD8oKyt0aGlzLnNpemUsci5wdXNoKFtlLHRdKSk6cltpXVsxXT10LHRoaXN9LEtyLnByb3RvdHlwZS5jbGVhcj1mdW5jdGlvbigpe3RoaXMuc2l6ZT0wLHRoaXMuX19kYXRhX189e2hhc2g6bmV3IE5yLG1hcDpuZXcod3J8fHpyKSxzdHJpbmc6bmV3IE5yfX0sS3IucHJvdG90eXBlLmRlbGV0ZT1mdW5jdGlvbihlKXt2YXIgdD1hbyh0aGlzLGUpLmRlbGV0ZShlKTtyZXR1cm4gdGhpcy5zaXplLT10PzE6MCx0fSxLci5wcm90b3R5cGUuZ2V0PWZ1bmN0aW9uKGUpe3JldHVybiBhbyh0aGlzLGUpLmdldChlKX0sS3IucHJvdG90eXBlLmhhcz1mdW5jdGlvbihlKXtyZXR1cm4gYW8odGhpcyxlKS5oYXMoZSl9LEtyLnByb3RvdHlwZS5zZXQ9ZnVuY3Rpb24oZSx0KXt2YXIgcj1hbyh0aGlzLGUpLGk9ci5zaXplO3JldHVybiByLnNldChlLHQpLHRoaXMuc2l6ZSs9ci5zaXplPT1pPzA6MSx0aGlzfSxWci5wcm90b3R5cGUuYWRkPVZyLnByb3RvdHlwZS5wdXNoPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9fZGF0YV9fLnNldChlLHMpLHRoaXN9LFZyLnByb3RvdHlwZS5oYXM9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX19kYXRhX18uaGFzKGUpfSxHci5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXt0aGlzLl9fZGF0YV9fPW5ldyB6cix0aGlzLnNpemU9MH0sR3IucHJvdG90eXBlLmRlbGV0ZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9fZGF0YV9fLHI9dC5kZWxldGUoZSk7cmV0dXJuIHRoaXMuc2l6ZT10LnNpemUscn0sR3IucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fX2RhdGFfXy5nZXQoZSl9LEdyLnByb3RvdHlwZS5oYXM9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX19kYXRhX18uaGFzKGUpfSxHci5wcm90b3R5cGUuc2V0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5fX2RhdGFfXztpZihyIGluc3RhbmNlb2YgenIpe3ZhciBpPXIuX19kYXRhX187aWYoIXdyfHxpLmxlbmd0aDwxOTkpcmV0dXJuIGkucHVzaChbZSx0XSksdGhpcy5zaXplPSsrci5zaXplLHRoaXM7cj10aGlzLl9fZGF0YV9fPW5ldyBLcihpKX1yZXR1cm4gci5zZXQoZSx0KSx0aGlzLnNpemU9ci5zaXplLHRoaXN9O3ZhciB1aT1Ubih5aSksaGk9VG4obWksITApO2Z1bmN0aW9uIGZpKGUsdCl7dmFyIHI9ITA7cmV0dXJuIHVpKGUsKGZ1bmN0aW9uKGUsaSxuKXtyZXR1cm4gcj0hIXQoZSxpLG4pfSkpLHJ9ZnVuY3Rpb24gX2koZSx0LHIpe2Zvcih2YXIgaT0tMSxvPWUubGVuZ3RoOysraTxvOyl7dmFyIHM9ZVtpXSxhPXQocyk7aWYobnVsbCE9YSYmKGM9PT1uP2E9PWEmJiFsYShhKTpyKGEsYykpKXZhciBjPWEsbD1zfXJldHVybiBsfWZ1bmN0aW9uIGRpKGUsdCl7dmFyIHI9W107cmV0dXJuIHVpKGUsKGZ1bmN0aW9uKGUsaSxuKXt0KGUsaSxuKSYmci5wdXNoKGUpfSkpLHJ9ZnVuY3Rpb24gcGkoZSx0LHIsaSxuKXt2YXIgbz0tMSxzPWUubGVuZ3RoO2ZvcihyfHwocj12byksbnx8KG49W10pOysrbzxzOyl7dmFyIGE9ZVtvXTt0PjAmJnIoYSk/dD4xP3BpKGEsdC0xLHIsaSxuKTp4dChuLGEpOml8fChuW24ubGVuZ3RoXT1hKX1yZXR1cm4gbn12YXIgdmk9T24oKSxnaT1PbighMCk7ZnVuY3Rpb24geWkoZSx0KXtyZXR1cm4gZSYmdmkoZSx0LE9hKX1mdW5jdGlvbiBtaShlLHQpe3JldHVybiBlJiZnaShlLHQsT2EpfWZ1bmN0aW9uIGJpKGUsdCl7cmV0dXJuIEN0KHQsKGZ1bmN0aW9uKHQpe3JldHVybiAkcyhlW3RdKX0pKX1mdW5jdGlvbiBTaShlLHQpe2Zvcih2YXIgcj0wLGk9KHQ9Z24odCxlKSkubGVuZ3RoO251bGwhPWUmJnI8aTspZT1lW2pvKHRbcisrXSldO3JldHVybiByJiZyPT1pP2U6bn1mdW5jdGlvbiBDaShlLHQscil7dmFyIGk9dChlKTtyZXR1cm4gS3MoZSk/aTp4dChpLHIoZSkpfWZ1bmN0aW9uIHdpKGUpe3JldHVybiBudWxsPT1lP2U9PT1uPyJbb2JqZWN0IFVuZGVmaW5lZF0iOiJbb2JqZWN0IE51bGxdIjphdCYmYXQgaW4gTGUoZSk/ZnVuY3Rpb24oZSl7dmFyIHQ9QmUuY2FsbChlLGF0KSxyPWVbYXRdO3RyeXtlW2F0XT1uO3ZhciBpPSEwfWNhdGNoKGUpe312YXIgbz1JZS5jYWxsKGUpO3JldHVybiBpJiYodD9lW2F0XT1yOmRlbGV0ZSBlW2F0XSksb30oZSk6ZnVuY3Rpb24oZSl7cmV0dXJuIEllLmNhbGwoZSl9KGUpfWZ1bmN0aW9uIExpKGUsdCl7cmV0dXJuIGU+dH1mdW5jdGlvbiBFaShlLHQpe3JldHVybiBudWxsIT1lJiZCZS5jYWxsKGUsdCl9ZnVuY3Rpb24geGkoZSx0KXtyZXR1cm4gbnVsbCE9ZSYmdCBpbiBMZShlKX1mdW5jdGlvbiBBaShlLHQscil7Zm9yKHZhciBvPXI/THQ6d3Qscz1lWzBdLmxlbmd0aCxhPWUubGVuZ3RoLGM9YSxsPWkoYSksdT0xLzAsaD1bXTtjLS07KXt2YXIgZj1lW2NdO2MmJnQmJihmPUV0KGYsTnQodCkpKSx1PWdyKGYubGVuZ3RoLHUpLGxbY109IXImJih0fHxzPj0xMjAmJmYubGVuZ3RoPj0xMjApP25ldyBWcihjJiZmKTpufWY9ZVswXTt2YXIgXz0tMSxkPWxbMF07ZTpmb3IoOysrXzxzJiZoLmxlbmd0aDx1Oyl7dmFyIHA9ZltfXSx2PXQ/dChwKTpwO2lmKHA9cnx8MCE9PXA/cDowLCEoZD9LdChkLHYpOm8oaCx2LHIpKSl7Zm9yKGM9YTstLWM7KXt2YXIgZz1sW2NdO2lmKCEoZz9LdChnLHYpOm8oZVtjXSx2LHIpKSljb250aW51ZSBlfWQmJmQucHVzaCh2KSxoLnB1c2gocCl9fXJldHVybiBofWZ1bmN0aW9uIGtpKGUsdCxyKXt2YXIgaT1udWxsPT0oZT14byhlLHQ9Z24odCxlKSkpP2U6ZVtqbyhKbyh0KSldO3JldHVybiBudWxsPT1pP246Z3QoaSxlLHIpfWZ1bmN0aW9uIE1pKGUpe3JldHVybiByYShlKSYmd2koZSk9PXB9ZnVuY3Rpb24gUmkoZSx0LHIsaSxvKXtyZXR1cm4gZT09PXR8fChudWxsPT1lfHxudWxsPT10fHwhcmEoZSkmJiFyYSh0KT9lIT1lJiZ0IT10OmZ1bmN0aW9uKGUsdCxyLGksbyxzKXt2YXIgYT1LcyhlKSxjPUtzKHQpLGw9YT92OmZvKGUpLHU9Yz92OmZvKHQpLGg9KGw9bD09cD9MOmwpPT1MLGY9KHU9dT09cD9MOnUpPT1MLF89bD09dTtpZihfJiZYcyhlKSl7aWYoIVhzKHQpKXJldHVybiExO2E9ITAsaD0hMX1pZihfJiYhaClyZXR1cm4gc3x8KHM9bmV3IEdyKSxhfHx1YShlKT9RbihlLHQscixpLG8scyk6ZnVuY3Rpb24oZSx0LHIsaSxuLG8scyl7c3dpdGNoKHIpe2Nhc2UgTzppZihlLmJ5dGVMZW5ndGghPXQuYnl0ZUxlbmd0aHx8ZS5ieXRlT2Zmc2V0IT10LmJ5dGVPZmZzZXQpcmV0dXJuITE7ZT1lLmJ1ZmZlcix0PXQuYnVmZmVyO2Nhc2UgVDpyZXR1cm4hKGUuYnl0ZUxlbmd0aCE9dC5ieXRlTGVuZ3RofHwhbyhuZXcgcWUoZSksbmV3IHFlKHQpKSk7Y2FzZSBnOmNhc2UgeTpjYXNlIHc6cmV0dXJuIFVzKCtlLCt0KTtjYXNlIG06cmV0dXJuIGUubmFtZT09dC5uYW1lJiZlLm1lc3NhZ2U9PXQubWVzc2FnZTtjYXNlIHg6Y2FzZSBrOnJldHVybiBlPT10KyIiO2Nhc2UgQzp2YXIgYT1RdDtjYXNlIEE6dmFyIGM9MSZpO2lmKGF8fChhPXJyKSxlLnNpemUhPXQuc2l6ZSYmIWMpcmV0dXJuITE7dmFyIGw9cy5nZXQoZSk7aWYobClyZXR1cm4gbD09dDtpfD0yLHMuc2V0KGUsdCk7dmFyIHU9UW4oYShlKSxhKHQpLGksbixvLHMpO3JldHVybiBzLmRlbGV0ZShlKSx1O2Nhc2UgTTppZihJcilyZXR1cm4gSXIuY2FsbChlKT09SXIuY2FsbCh0KX1yZXR1cm4hMX0oZSx0LGwscixpLG8scyk7aWYoISgxJnIpKXt2YXIgZD1oJiZCZS5jYWxsKGUsIl9fd3JhcHBlZF9fIiksYj1mJiZCZS5jYWxsKHQsIl9fd3JhcHBlZF9fIik7aWYoZHx8Yil7dmFyIFM9ZD9lLnZhbHVlKCk6ZSxFPWI/dC52YWx1ZSgpOnQ7cmV0dXJuIHN8fChzPW5ldyBHciksbyhTLEUscixpLHMpfX1yZXR1cm4hIV8mJihzfHwocz1uZXcgR3IpLGZ1bmN0aW9uKGUsdCxyLGksbyxzKXt2YXIgYT0xJnIsYz10byhlKSxsPWMubGVuZ3RoO2lmKGwhPXRvKHQpLmxlbmd0aCYmIWEpcmV0dXJuITE7Zm9yKHZhciB1PWw7dS0tOyl7dmFyIGg9Y1t1XTtpZighKGE/aCBpbiB0OkJlLmNhbGwodCxoKSkpcmV0dXJuITF9dmFyIGY9cy5nZXQoZSksXz1zLmdldCh0KTtpZihmJiZfKXJldHVybiBmPT10JiZfPT1lO3ZhciBkPSEwO3Muc2V0KGUsdCkscy5zZXQodCxlKTtmb3IodmFyIHA9YTsrK3U8bDspe3ZhciB2PWVbaD1jW3VdXSxnPXRbaF07aWYoaSl2YXIgeT1hP2koZyx2LGgsdCxlLHMpOmkodixnLGgsZSx0LHMpO2lmKCEoeT09PW4/dj09PWd8fG8odixnLHIsaSxzKTp5KSl7ZD0hMTticmVha31wfHwocD0iY29uc3RydWN0b3IiPT1oKX1pZihkJiYhcCl7dmFyIG09ZS5jb25zdHJ1Y3RvcixiPXQuY29uc3RydWN0b3I7bT09Ynx8ISgiY29uc3RydWN0b3IiaW4gZSl8fCEoImNvbnN0cnVjdG9yImluIHQpfHwiZnVuY3Rpb24iPT10eXBlb2YgbSYmbSBpbnN0YW5jZW9mIG0mJiJmdW5jdGlvbiI9PXR5cGVvZiBiJiZiIGluc3RhbmNlb2YgYnx8KGQ9ITEpfXJldHVybiBzLmRlbGV0ZShlKSxzLmRlbGV0ZSh0KSxkfShlLHQscixpLG8scykpfShlLHQscixpLFJpLG8pKX1mdW5jdGlvbiBUaShlLHQscixpKXt2YXIgbz1yLmxlbmd0aCxzPW8sYT0haTtpZihudWxsPT1lKXJldHVybiFzO2ZvcihlPUxlKGUpO28tLTspe3ZhciBjPXJbb107aWYoYSYmY1syXT9jWzFdIT09ZVtjWzBdXTohKGNbMF1pbiBlKSlyZXR1cm4hMX1mb3IoOysrbzxzOyl7dmFyIGw9KGM9cltvXSlbMF0sdT1lW2xdLGg9Y1sxXTtpZihhJiZjWzJdKXtpZih1PT09biYmIShsIGluIGUpKXJldHVybiExfWVsc2V7dmFyIGY9bmV3IEdyO2lmKGkpdmFyIF89aSh1LGgsbCxlLHQsZik7aWYoIShfPT09bj9SaShoLHUsMyxpLGYpOl8pKXJldHVybiExfX1yZXR1cm4hMH1mdW5jdGlvbiBPaShlKXtyZXR1cm4hKCF0YShlKXx8KHQ9ZSxQZSYmUGUgaW4gdCkpJiYoJHMoZSk/RmU6cGUpLnRlc3QoRm8oZSkpO3ZhciB0fWZ1bmN0aW9uIEJpKGUpe3JldHVybiJmdW5jdGlvbiI9PXR5cGVvZiBlP2U6bnVsbD09ZT9uYzoib2JqZWN0Ij09dHlwZW9mIGU/S3MoZSk/amkoZVswXSxlWzFdKTpIaShlKTpfYyhlKX1mdW5jdGlvbiBEaShlKXtpZighQ28oZSkpcmV0dXJuIHByKGUpO3ZhciB0PVtdO2Zvcih2YXIgciBpbiBMZShlKSlCZS5jYWxsKGUscikmJiJjb25zdHJ1Y3RvciIhPXImJnQucHVzaChyKTtyZXR1cm4gdH1mdW5jdGlvbiBQaShlLHQpe3JldHVybiBlPHR9ZnVuY3Rpb24gSWkoZSx0KXt2YXIgcj0tMSxuPUdzKGUpP2koZS5sZW5ndGgpOltdO3JldHVybiB1aShlLChmdW5jdGlvbihlLGksbyl7blsrK3JdPXQoZSxpLG8pfSkpLG59ZnVuY3Rpb24gSGkoZSl7dmFyIHQ9Y28oZSk7cmV0dXJuIDE9PXQubGVuZ3RoJiZ0WzBdWzJdP0xvKHRbMF1bMF0sdFswXVsxXSk6ZnVuY3Rpb24ocil7cmV0dXJuIHI9PT1lfHxUaShyLGUsdCl9fWZ1bmN0aW9uIGppKGUsdCl7cmV0dXJuIG1vKGUpJiZ3byh0KT9MbyhqbyhlKSx0KTpmdW5jdGlvbihyKXt2YXIgaT1BYShyLGUpO3JldHVybiBpPT09biYmaT09PXQ/a2EocixlKTpSaSh0LGksMyl9fWZ1bmN0aW9uIEZpKGUsdCxyLGksbyl7ZSE9PXQmJnZpKHQsKGZ1bmN0aW9uKHMsYSl7aWYob3x8KG89bmV3IEdyKSx0YShzKSkhZnVuY3Rpb24oZSx0LHIsaSxvLHMsYSl7dmFyIGM9a28oZSxyKSxsPWtvKHQsciksdT1hLmdldChsKTtpZih1KSRyKGUscix1KTtlbHNle3ZhciBoPXM/cyhjLGwscisiIixlLHQsYSk6bixmPWg9PT1uO2lmKGYpe3ZhciBfPUtzKGwpLGQ9IV8mJlhzKGwpLHA9IV8mJiFkJiZ1YShsKTtoPWwsX3x8ZHx8cD9LcyhjKT9oPWM6WXMoYyk/aD1BbihjKTpkPyhmPSExLGg9U24obCwhMCkpOnA/KGY9ITEsaD13bihsLCEwKSk6aD1bXTpvYShsKXx8enMobCk/KGg9Yyx6cyhjKT9oPXlhKGMpOnRhKGMpJiYhJHMoYyl8fChoPXBvKGwpKSk6Zj0hMX1mJiYoYS5zZXQobCxoKSxvKGgsbCxpLHMsYSksYS5kZWxldGUobCkpLCRyKGUscixoKX19KGUsdCxhLHIsRmksaSxvKTtlbHNle3ZhciBjPWk/aShrbyhlLGEpLHMsYSsiIixlLHQsbyk6bjtjPT09biYmKGM9cyksJHIoZSxhLGMpfX0pLEJhKX1mdW5jdGlvbiBXaShlLHQpe3ZhciByPWUubGVuZ3RoO2lmKHIpcmV0dXJuIGdvKHQrPXQ8MD9yOjAscik/ZVt0XTpufWZ1bmN0aW9uIFVpKGUsdCxyKXt0PXQubGVuZ3RoP0V0KHQsKGZ1bmN0aW9uKGUpe3JldHVybiBLcyhlKT9mdW5jdGlvbih0KXtyZXR1cm4gU2kodCwxPT09ZS5sZW5ndGg/ZVswXTplKX06ZX0pKTpbbmNdO3ZhciBpPS0xO3Q9RXQodCxOdChzbygpKSk7dmFyIG49SWkoZSwoZnVuY3Rpb24oZSxyLG4pe3ZhciBvPUV0KHQsKGZ1bmN0aW9uKHQpe3JldHVybiB0KGUpfSkpO3JldHVybntjcml0ZXJpYTpvLGluZGV4OisraSx2YWx1ZTplfX0pKTtyZXR1cm4gZnVuY3Rpb24oZSx0KXt2YXIgaT1lLmxlbmd0aDtmb3IoZS5zb3J0KChmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihlLHQscil7Zm9yKHZhciBpPS0xLG49ZS5jcml0ZXJpYSxvPXQuY3JpdGVyaWEscz1uLmxlbmd0aCxhPXIubGVuZ3RoOysraTxzOyl7dmFyIGM9TG4obltpXSxvW2ldKTtpZihjKXJldHVybiBpPj1hP2M6YyooImRlc2MiPT1yW2ldPy0xOjEpfXJldHVybiBlLmluZGV4LXQuaW5kZXh9KGUsdCxyKX0pKTtpLS07KWVbaV09ZVtpXS52YWx1ZTtyZXR1cm4gZX0obil9ZnVuY3Rpb24gcWkoZSx0LHIpe2Zvcih2YXIgaT0tMSxuPXQubGVuZ3RoLG89e307KytpPG47KXt2YXIgcz10W2ldLGE9U2koZSxzKTtyKGEscykmJlppKG8sZ24ocyxlKSxhKX1yZXR1cm4gb31mdW5jdGlvbiBOaShlLHQscixpKXt2YXIgbj1pP0R0OkJ0LG89LTEscz10Lmxlbmd0aCxhPWU7Zm9yKGU9PT10JiYodD1Bbih0KSksciYmKGE9RXQoZSxOdChyKSkpOysrbzxzOylmb3IodmFyIGM9MCxsPXRbb10sdT1yP3IobCk6bDsoYz1uKGEsdSxjLGkpKT4tMTspYSE9PWUmJml0LmNhbGwoYSxjLDEpLGl0LmNhbGwoZSxjLDEpO3JldHVybiBlfWZ1bmN0aW9uIHppKGUsdCl7Zm9yKHZhciByPWU/dC5sZW5ndGg6MCxpPXItMTtyLS07KXt2YXIgbj10W3JdO2lmKHI9PWl8fG4hPT1vKXt2YXIgbz1uO2dvKG4pP2l0LmNhbGwoZSxuLDEpOmxuKGUsbil9fXJldHVybiBlfWZ1bmN0aW9uIEtpKGUsdCl7cmV0dXJuIGUrdXIoYnIoKSoodC1lKzEpKX1mdW5jdGlvbiBWaShlLHQpe3ZhciByPSIiO2lmKCFlfHx0PDF8fHQ+aClyZXR1cm4gcjtkb3t0JTImJihyKz1lKSwodD11cih0LzIpKSYmKGUrPWUpfXdoaWxlKHQpO3JldHVybiByfWZ1bmN0aW9uIEdpKGUsdCl7cmV0dXJuIFRvKEVvKGUsdCxuYyksZSsiIil9ZnVuY3Rpb24gWWkoZSl7cmV0dXJuIFhyKFVhKGUpKX1mdW5jdGlvbiBYaShlLHQpe3ZhciByPVVhKGUpO3JldHVybiBEbyhyLG9pKHQsMCxyLmxlbmd0aCkpfWZ1bmN0aW9uIFppKGUsdCxyLGkpe2lmKCF0YShlKSlyZXR1cm4gZTtmb3IodmFyIG89LTEscz0odD1nbih0LGUpKS5sZW5ndGgsYT1zLTEsYz1lO251bGwhPWMmJisrbzxzOyl7dmFyIGw9am8odFtvXSksdT1yO2lmKCJfX3Byb3RvX18iPT09bHx8ImNvbnN0cnVjdG9yIj09PWx8fCJwcm90b3R5cGUiPT09bClyZXR1cm4gZTtpZihvIT1hKXt2YXIgaD1jW2xdOyh1PWk/aShoLGwsYyk6bik9PT1uJiYodT10YShoKT9oOmdvKHRbbysxXSk/W106e30pfVFyKGMsbCx1KSxjPWNbbF19cmV0dXJuIGV9dmFyIEppPWtyP2Z1bmN0aW9uKGUsdCl7cmV0dXJuIGtyLnNldChlLHQpLGV9Om5jLCRpPWx0P2Z1bmN0aW9uKGUsdCl7cmV0dXJuIGx0KGUsInRvU3RyaW5nIix7Y29uZmlndXJhYmxlOiEwLGVudW1lcmFibGU6ITEsdmFsdWU6dGModCksd3JpdGFibGU6ITB9KX06bmM7ZnVuY3Rpb24gUWkoZSl7cmV0dXJuIERvKFVhKGUpKX1mdW5jdGlvbiBlbihlLHQscil7dmFyIG49LTEsbz1lLmxlbmd0aDt0PDAmJih0PS10Pm8/MDpvK3QpLChyPXI+bz9vOnIpPDAmJihyKz1vKSxvPXQ+cj8wOnItdD4+PjAsdD4+Pj0wO2Zvcih2YXIgcz1pKG8pOysrbjxvOylzW25dPWVbbit0XTtyZXR1cm4gc31mdW5jdGlvbiB0bihlLHQpe3ZhciByO3JldHVybiB1aShlLChmdW5jdGlvbihlLGksbil7cmV0dXJuIShyPXQoZSxpLG4pKX0pKSwhIXJ9ZnVuY3Rpb24gcm4oZSx0LHIpe3ZhciBpPTAsbj1udWxsPT1lP2k6ZS5sZW5ndGg7aWYoIm51bWJlciI9PXR5cGVvZiB0JiZ0PT10JiZuPD0yMTQ3NDgzNjQ3KXtmb3IoO2k8bjspe3ZhciBvPWkrbj4+PjEscz1lW29dO251bGwhPT1zJiYhbGEocykmJihyP3M8PXQ6czx0KT9pPW8rMTpuPW99cmV0dXJuIG59cmV0dXJuIG5uKGUsdCxuYyxyKX1mdW5jdGlvbiBubihlLHQscixpKXt2YXIgbz0wLHM9bnVsbD09ZT8wOmUubGVuZ3RoO2lmKDA9PT1zKXJldHVybiAwO2Zvcih2YXIgYT0odD1yKHQpKSE9dCxjPW51bGw9PT10LGw9bGEodCksdT10PT09bjtvPHM7KXt2YXIgaD11cigobytzKS8yKSxmPXIoZVtoXSksXz1mIT09bixkPW51bGw9PT1mLHA9Zj09Zix2PWxhKGYpO2lmKGEpdmFyIGc9aXx8cDtlbHNlIGc9dT9wJiYoaXx8Xyk6Yz9wJiZfJiYoaXx8IWQpOmw/cCYmXyYmIWQmJihpfHwhdik6IWQmJiF2JiYoaT9mPD10OmY8dCk7Zz9vPWgrMTpzPWh9cmV0dXJuIGdyKHMsNDI5NDk2NzI5NCl9ZnVuY3Rpb24gb24oZSx0KXtmb3IodmFyIHI9LTEsaT1lLmxlbmd0aCxuPTAsbz1bXTsrK3I8aTspe3ZhciBzPWVbcl0sYT10P3Qocyk6cztpZighcnx8IVVzKGEsYykpe3ZhciBjPWE7b1tuKytdPTA9PT1zPzA6c319cmV0dXJuIG99ZnVuY3Rpb24gc24oZSl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlP2U6bGEoZSk/ZjorZX1mdW5jdGlvbiBhbihlKXtpZigic3RyaW5nIj09dHlwZW9mIGUpcmV0dXJuIGU7aWYoS3MoZSkpcmV0dXJuIEV0KGUsYW4pKyIiO2lmKGxhKGUpKXJldHVybiBIcj9Ici5jYWxsKGUpOiIiO3ZhciB0PWUrIiI7cmV0dXJuIjAiPT10JiYxL2U9PS0xLzA/Ii0wIjp0fWZ1bmN0aW9uIGNuKGUsdCxyKXt2YXIgaT0tMSxuPXd0LG89ZS5sZW5ndGgscz0hMCxhPVtdLGM9YTtpZihyKXM9ITEsbj1MdDtlbHNlIGlmKG8+PTIwMCl7dmFyIGw9dD9udWxsOkduKGUpO2lmKGwpcmV0dXJuIHJyKGwpO3M9ITEsbj1LdCxjPW5ldyBWcn1lbHNlIGM9dD9bXTphO2U6Zm9yKDsrK2k8bzspe3ZhciB1PWVbaV0saD10P3QodSk6dTtpZih1PXJ8fDAhPT11P3U6MCxzJiZoPT1oKXtmb3IodmFyIGY9Yy5sZW5ndGg7Zi0tOylpZihjW2ZdPT09aCljb250aW51ZSBlO3QmJmMucHVzaChoKSxhLnB1c2godSl9ZWxzZSBuKGMsaCxyKXx8KGMhPT1hJiZjLnB1c2goaCksYS5wdXNoKHUpKX1yZXR1cm4gYX1mdW5jdGlvbiBsbihlLHQpe3JldHVybiBudWxsPT0oZT14byhlLHQ9Z24odCxlKSkpfHxkZWxldGUgZVtqbyhKbyh0KSldfWZ1bmN0aW9uIHVuKGUsdCxyLGkpe3JldHVybiBaaShlLHQscihTaShlLHQpKSxpKX1mdW5jdGlvbiBobihlLHQscixpKXtmb3IodmFyIG49ZS5sZW5ndGgsbz1pP246LTE7KGk/by0tOisrbzxuKSYmdChlW29dLG8sZSk7KTtyZXR1cm4gcj9lbihlLGk/MDpvLGk/bysxOm4pOmVuKGUsaT9vKzE6MCxpP246byl9ZnVuY3Rpb24gZm4oZSx0KXt2YXIgcj1lO3JldHVybiByIGluc3RhbmNlb2YgcXImJihyPXIudmFsdWUoKSksQXQodCwoZnVuY3Rpb24oZSx0KXtyZXR1cm4gdC5mdW5jLmFwcGx5KHQudGhpc0FyZyx4dChbZV0sdC5hcmdzKSl9KSxyKX1mdW5jdGlvbiBfbihlLHQscil7dmFyIG49ZS5sZW5ndGg7aWYobjwyKXJldHVybiBuP2NuKGVbMF0pOltdO2Zvcih2YXIgbz0tMSxzPWkobik7KytvPG47KWZvcih2YXIgYT1lW29dLGM9LTE7KytjPG47KWMhPW8mJihzW29dPWxpKHNbb118fGEsZVtjXSx0LHIpKTtyZXR1cm4gY24ocGkocywxKSx0LHIpfWZ1bmN0aW9uIGRuKGUsdCxyKXtmb3IodmFyIGk9LTEsbz1lLmxlbmd0aCxzPXQubGVuZ3RoLGE9e307KytpPG87KXt2YXIgYz1pPHM/dFtpXTpuO3IoYSxlW2ldLGMpfXJldHVybiBhfWZ1bmN0aW9uIHBuKGUpe3JldHVybiBZcyhlKT9lOltdfWZ1bmN0aW9uIHZuKGUpe3JldHVybiJmdW5jdGlvbiI9PXR5cGVvZiBlP2U6bmN9ZnVuY3Rpb24gZ24oZSx0KXtyZXR1cm4gS3MoZSk/ZTptbyhlLHQpP1tlXTpIbyhtYShlKSl9dmFyIHluPUdpO2Z1bmN0aW9uIG1uKGUsdCxyKXt2YXIgaT1lLmxlbmd0aDtyZXR1cm4gcj1yPT09bj9pOnIsIXQmJnI+PWk/ZTplbihlLHQscil9dmFyIGJuPXV0fHxmdW5jdGlvbihlKXtyZXR1cm4gb3QuY2xlYXJUaW1lb3V0KGUpfTtmdW5jdGlvbiBTbihlLHQpe2lmKHQpcmV0dXJuIGUuc2xpY2UoKTt2YXIgcj1lLmxlbmd0aCxpPU5lP05lKHIpOm5ldyBlLmNvbnN0cnVjdG9yKHIpO3JldHVybiBlLmNvcHkoaSksaX1mdW5jdGlvbiBDbihlKXt2YXIgdD1uZXcgZS5jb25zdHJ1Y3RvcihlLmJ5dGVMZW5ndGgpO3JldHVybiBuZXcgcWUodCkuc2V0KG5ldyBxZShlKSksdH1mdW5jdGlvbiB3bihlLHQpe3ZhciByPXQ/Q24oZS5idWZmZXIpOmUuYnVmZmVyO3JldHVybiBuZXcgZS5jb25zdHJ1Y3RvcihyLGUuYnl0ZU9mZnNldCxlLmxlbmd0aCl9ZnVuY3Rpb24gTG4oZSx0KXtpZihlIT09dCl7dmFyIHI9ZSE9PW4saT1udWxsPT09ZSxvPWU9PWUscz1sYShlKSxhPXQhPT1uLGM9bnVsbD09PXQsbD10PT10LHU9bGEodCk7aWYoIWMmJiF1JiYhcyYmZT50fHxzJiZhJiZsJiYhYyYmIXV8fGkmJmEmJmx8fCFyJiZsfHwhbylyZXR1cm4gMTtpZighaSYmIXMmJiF1JiZlPHR8fHUmJnImJm8mJiFpJiYhc3x8YyYmciYmb3x8IWEmJm98fCFsKXJldHVybi0xfXJldHVybiAwfWZ1bmN0aW9uIEVuKGUsdCxyLG4pe2Zvcih2YXIgbz0tMSxzPWUubGVuZ3RoLGE9ci5sZW5ndGgsYz0tMSxsPXQubGVuZ3RoLHU9dnIocy1hLDApLGg9aShsK3UpLGY9IW47KytjPGw7KWhbY109dFtjXTtmb3IoOysrbzxhOykoZnx8bzxzKSYmKGhbcltvXV09ZVtvXSk7Zm9yKDt1LS07KWhbYysrXT1lW28rK107cmV0dXJuIGh9ZnVuY3Rpb24geG4oZSx0LHIsbil7Zm9yKHZhciBvPS0xLHM9ZS5sZW5ndGgsYT0tMSxjPXIubGVuZ3RoLGw9LTEsdT10Lmxlbmd0aCxoPXZyKHMtYywwKSxmPWkoaCt1KSxfPSFuOysrbzxoOylmW29dPWVbb107Zm9yKHZhciBkPW87KytsPHU7KWZbZCtsXT10W2xdO2Zvcig7KythPGM7KShffHxvPHMpJiYoZltkK3JbYV1dPWVbbysrXSk7cmV0dXJuIGZ9ZnVuY3Rpb24gQW4oZSx0KXt2YXIgcj0tMSxuPWUubGVuZ3RoO2Zvcih0fHwodD1pKG4pKTsrK3I8bjspdFtyXT1lW3JdO3JldHVybiB0fWZ1bmN0aW9uIGtuKGUsdCxyLGkpe3ZhciBvPSFyO3J8fChyPXt9KTtmb3IodmFyIHM9LTEsYT10Lmxlbmd0aDsrK3M8YTspe3ZhciBjPXRbc10sbD1pP2kocltjXSxlW2NdLGMscixlKTpuO2w9PT1uJiYobD1lW2NdKSxvP2lpKHIsYyxsKTpRcihyLGMsbCl9cmV0dXJuIHJ9ZnVuY3Rpb24gTW4oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt2YXIgbj1LcyhyKT95dDp0aSxvPXQ/dCgpOnt9O3JldHVybiBuKHIsZSxzbyhpLDIpLG8pfX1mdW5jdGlvbiBSbihlKXtyZXR1cm4gR2koKGZ1bmN0aW9uKHQscil7dmFyIGk9LTEsbz1yLmxlbmd0aCxzPW8+MT9yW28tMV06bixhPW8+Mj9yWzJdOm47Zm9yKHM9ZS5sZW5ndGg+MyYmImZ1bmN0aW9uIj09dHlwZW9mIHM/KG8tLSxzKTpuLGEmJnlvKHJbMF0sclsxXSxhKSYmKHM9bzwzP246cyxvPTEpLHQ9TGUodCk7KytpPG87KXt2YXIgYz1yW2ldO2MmJmUodCxjLGkscyl9cmV0dXJuIHR9KSl9ZnVuY3Rpb24gVG4oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXtpZihudWxsPT1yKXJldHVybiByO2lmKCFHcyhyKSlyZXR1cm4gZShyLGkpO2Zvcih2YXIgbj1yLmxlbmd0aCxvPXQ/bjotMSxzPUxlKHIpOyh0P28tLTorK288bikmJiExIT09aShzW29dLG8scyk7KTtyZXR1cm4gcn19ZnVuY3Rpb24gT24oZSl7cmV0dXJuIGZ1bmN0aW9uKHQscixpKXtmb3IodmFyIG49LTEsbz1MZSh0KSxzPWkodCksYT1zLmxlbmd0aDthLS07KXt2YXIgYz1zW2U/YTorK25dO2lmKCExPT09cihvW2NdLGMsbykpYnJlYWt9cmV0dXJuIHR9fWZ1bmN0aW9uIEJuKGUpe3JldHVybiBmdW5jdGlvbih0KXt2YXIgcj0kdCh0PW1hKHQpKT9vcih0KTpuLGk9cj9yWzBdOnQuY2hhckF0KDApLG89cj9tbihyLDEpLmpvaW4oIiIpOnQuc2xpY2UoMSk7cmV0dXJuIGlbZV0oKStvfX1mdW5jdGlvbiBEbihlKXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIEF0KCRhKHphKHQpLnJlcGxhY2UoemUsIiIpKSxlLCIiKX19ZnVuY3Rpb24gUG4oZSl7cmV0dXJuIGZ1bmN0aW9uKCl7dmFyIHQ9YXJndW1lbnRzO3N3aXRjaCh0Lmxlbmd0aCl7Y2FzZSAwOnJldHVybiBuZXcgZTtjYXNlIDE6cmV0dXJuIG5ldyBlKHRbMF0pO2Nhc2UgMjpyZXR1cm4gbmV3IGUodFswXSx0WzFdKTtjYXNlIDM6cmV0dXJuIG5ldyBlKHRbMF0sdFsxXSx0WzJdKTtjYXNlIDQ6cmV0dXJuIG5ldyBlKHRbMF0sdFsxXSx0WzJdLHRbM10pO2Nhc2UgNTpyZXR1cm4gbmV3IGUodFswXSx0WzFdLHRbMl0sdFszXSx0WzRdKTtjYXNlIDY6cmV0dXJuIG5ldyBlKHRbMF0sdFsxXSx0WzJdLHRbM10sdFs0XSx0WzVdKTtjYXNlIDc6cmV0dXJuIG5ldyBlKHRbMF0sdFsxXSx0WzJdLHRbM10sdFs0XSx0WzVdLHRbNl0pfXZhciByPUZyKGUucHJvdG90eXBlKSxpPWUuYXBwbHkocix0KTtyZXR1cm4gdGEoaSk/aTpyfX1mdW5jdGlvbiBJbihlKXtyZXR1cm4gZnVuY3Rpb24odCxyLGkpe3ZhciBvPUxlKHQpO2lmKCFHcyh0KSl7dmFyIHM9c28ociwzKTt0PU9hKHQpLHI9ZnVuY3Rpb24oZSl7cmV0dXJuIHMob1tlXSxlLG8pfX12YXIgYT1lKHQscixpKTtyZXR1cm4gYT4tMT9vW3M/dFthXTphXTpufX1mdW5jdGlvbiBIbihlKXtyZXR1cm4gZW8oKGZ1bmN0aW9uKHQpe3ZhciByPXQubGVuZ3RoLGk9cixzPVVyLnByb3RvdHlwZS50aHJ1O2ZvcihlJiZ0LnJldmVyc2UoKTtpLS07KXt2YXIgYT10W2ldO2lmKCJmdW5jdGlvbiIhPXR5cGVvZiBhKXRocm93IG5ldyBBZShvKTtpZihzJiYhYyYmIndyYXBwZXIiPT1ubyhhKSl2YXIgYz1uZXcgVXIoW10sITApfWZvcihpPWM/aTpyOysraTxyOyl7dmFyIGw9bm8oYT10W2ldKSx1PSJ3cmFwcGVyIj09bD9pbyhhKTpuO2M9dSYmYm8odVswXSkmJjQyND09dVsxXSYmIXVbNF0ubGVuZ3RoJiYxPT11WzldP2Nbbm8odVswXSldLmFwcGx5KGMsdVszXSk6MT09YS5sZW5ndGgmJmJvKGEpP2NbbF0oKTpjLnRocnUoYSl9cmV0dXJuIGZ1bmN0aW9uKCl7dmFyIGU9YXJndW1lbnRzLGk9ZVswXTtpZihjJiYxPT1lLmxlbmd0aCYmS3MoaSkpcmV0dXJuIGMucGxhbnQoaSkudmFsdWUoKTtmb3IodmFyIG49MCxvPXI/dFtuXS5hcHBseSh0aGlzLGUpOmk7KytuPHI7KW89dFtuXS5jYWxsKHRoaXMsbyk7cmV0dXJuIG99fSkpfWZ1bmN0aW9uIGpuKGUsdCxyLG8scyxhLGMsdSxoLGYpe3ZhciBfPXQmbCxkPTEmdCxwPTImdCx2PTI0JnQsZz01MTImdCx5PXA/bjpQbihlKTtyZXR1cm4gZnVuY3Rpb24gbigpe2Zvcih2YXIgbD1hcmd1bWVudHMubGVuZ3RoLG09aShsKSxiPWw7Yi0tOyltW2JdPWFyZ3VtZW50c1tiXTtpZih2KXZhciBTPW9vKG4pLEM9WXQobSxTKTtpZihvJiYobT1FbihtLG8scyx2KSksYSYmKG09eG4obSxhLGMsdikpLGwtPUMsdiYmbDxmKXt2YXIgdz10cihtLFMpO3JldHVybiBLbihlLHQsam4sbi5wbGFjZWhvbGRlcixyLG0sdyx1LGgsZi1sKX12YXIgTD1kP3I6dGhpcyxFPXA/TFtlXTplO3JldHVybiBsPW0ubGVuZ3RoLHU/bT1BbyhtLHUpOmcmJmw+MSYmbS5yZXZlcnNlKCksXyYmaDxsJiYobS5sZW5ndGg9aCksdGhpcyYmdGhpcyE9PW90JiZ0aGlzIGluc3RhbmNlb2YgbiYmKEU9eXx8UG4oRSkpLEUuYXBwbHkoTCxtKX19ZnVuY3Rpb24gRm4oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXtyZXR1cm4gZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuIHlpKGUsKGZ1bmN0aW9uKGUsbixvKXt0KGkscihlKSxuLG8pfSkpLGl9KHIsZSx0KGkpLHt9KX19ZnVuY3Rpb24gV24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt2YXIgbztpZihyPT09biYmaT09PW4pcmV0dXJuIHQ7aWYociE9PW4mJihvPXIpLGkhPT1uKXtpZihvPT09bilyZXR1cm4gaTsic3RyaW5nIj09dHlwZW9mIHJ8fCJzdHJpbmciPT10eXBlb2YgaT8ocj1hbihyKSxpPWFuKGkpKToocj1zbihyKSxpPXNuKGkpKSxvPWUocixpKX1yZXR1cm4gb319ZnVuY3Rpb24gVW4oZSl7cmV0dXJuIGVvKChmdW5jdGlvbih0KXtyZXR1cm4gdD1FdCh0LE50KHNvKCkpKSxHaSgoZnVuY3Rpb24ocil7dmFyIGk9dGhpcztyZXR1cm4gZSh0LChmdW5jdGlvbihlKXtyZXR1cm4gZ3QoZSxpLHIpfSkpfSkpfSkpfWZ1bmN0aW9uIHFuKGUsdCl7dmFyIHI9KHQ9dD09PW4/IiAiOmFuKHQpKS5sZW5ndGg7aWYocjwyKXJldHVybiByP1ZpKHQsZSk6dDt2YXIgaT1WaSh0LGxyKGUvbnIodCkpKTtyZXR1cm4gJHQodCk/bW4ob3IoaSksMCxlKS5qb2luKCIiKTppLnNsaWNlKDAsZSl9ZnVuY3Rpb24gTm4oZSl7cmV0dXJuIGZ1bmN0aW9uKHQscixvKXtyZXR1cm4gbyYmIm51bWJlciIhPXR5cGVvZiBvJiZ5byh0LHIsbykmJihyPW89biksdD1kYSh0KSxyPT09bj8ocj10LHQ9MCk6cj1kYShyKSxmdW5jdGlvbihlLHQscixuKXtmb3IodmFyIG89LTEscz12cihscigodC1lKS8ocnx8MSkpLDApLGE9aShzKTtzLS07KWFbbj9zOisrb109ZSxlKz1yO3JldHVybiBhfSh0LHIsbz1vPT09bj90PHI/MTotMTpkYShvKSxlKX19ZnVuY3Rpb24gem4oZSl7cmV0dXJuIGZ1bmN0aW9uKHQscil7cmV0dXJuInN0cmluZyI9PXR5cGVvZiB0JiYic3RyaW5nIj09dHlwZW9mIHJ8fCh0PWdhKHQpLHI9Z2EocikpLGUodCxyKX19ZnVuY3Rpb24gS24oZSx0LHIsaSxvLHMsYSxsLHUsaCl7dmFyIGY9OCZ0O3R8PWY/Yzo2NCw0Jih0Jj1+KGY/NjQ6YykpfHwodCY9LTQpO3ZhciBfPVtlLHQsbyxmP3M6bixmP2E6bixmP246cyxmP246YSxsLHUsaF0sZD1yLmFwcGx5KG4sXyk7cmV0dXJuIGJvKGUpJiZNbyhkLF8pLGQucGxhY2Vob2xkZXI9aSxPbyhkLGUsdCl9ZnVuY3Rpb24gVm4oZSl7dmFyIHQ9d2VbZV07cmV0dXJuIGZ1bmN0aW9uKGUscil7aWYoZT1nYShlKSwocj1udWxsPT1yPzA6Z3IocGEociksMjkyKSkmJl9yKGUpKXt2YXIgaT0obWEoZSkrImUiKS5zcGxpdCgiZSIpO3JldHVybisoKGk9KG1hKHQoaVswXSsiZSIrKCtpWzFdK3IpKSkrImUiKS5zcGxpdCgiZSIpKVswXSsiZSIrKCtpWzFdLXIpKX1yZXR1cm4gdChlKX19dmFyIEduPUVyJiYxL3JyKG5ldyBFcihbLC0wXSkpWzFdPT11P2Z1bmN0aW9uKGUpe3JldHVybiBuZXcgRXIoZSl9OmxjO2Z1bmN0aW9uIFluKGUpe3JldHVybiBmdW5jdGlvbih0KXt2YXIgcj1mbyh0KTtyZXR1cm4gcj09Qz9RdCh0KTpyPT1BP2lyKHQpOmZ1bmN0aW9uKGUsdCl7cmV0dXJuIEV0KHQsKGZ1bmN0aW9uKHQpe3JldHVyblt0LGVbdF1dfSkpfSh0LGUodCkpfX1mdW5jdGlvbiBYbihlLHQscixzLHUsaCxmLF8pe3ZhciBkPTImdDtpZighZCYmImZ1bmN0aW9uIiE9dHlwZW9mIGUpdGhyb3cgbmV3IEFlKG8pO3ZhciBwPXM/cy5sZW5ndGg6MDtpZihwfHwodCY9LTk3LHM9dT1uKSxmPWY9PT1uP2Y6dnIocGEoZiksMCksXz1fPT09bj9fOnBhKF8pLHAtPXU/dS5sZW5ndGg6MCw2NCZ0KXt2YXIgdj1zLGc9dTtzPXU9bn12YXIgeT1kP246aW8oZSksbT1bZSx0LHIscyx1LHYsZyxoLGYsX107aWYoeSYmZnVuY3Rpb24oZSx0KXt2YXIgcj1lWzFdLGk9dFsxXSxuPXJ8aSxvPW48MTMxLHM9aT09bCYmOD09cnx8aT09bCYmMjU2PT1yJiZlWzddLmxlbmd0aDw9dFs4XXx8Mzg0PT1pJiZ0WzddLmxlbmd0aDw9dFs4XSYmOD09cjtpZighbyYmIXMpcmV0dXJuIGU7MSZpJiYoZVsyXT10WzJdLG58PTEmcj8wOjQpO3ZhciBjPXRbM107aWYoYyl7dmFyIHU9ZVszXTtlWzNdPXU/RW4odSxjLHRbNF0pOmMsZVs0XT11P3RyKGVbM10sYSk6dFs0XX0oYz10WzVdKSYmKHU9ZVs1XSxlWzVdPXU/eG4odSxjLHRbNl0pOmMsZVs2XT11P3RyKGVbNV0sYSk6dFs2XSksKGM9dFs3XSkmJihlWzddPWMpLGkmbCYmKGVbOF09bnVsbD09ZVs4XT90WzhdOmdyKGVbOF0sdFs4XSkpLG51bGw9PWVbOV0mJihlWzldPXRbOV0pLGVbMF09dFswXSxlWzFdPW59KG0seSksZT1tWzBdLHQ9bVsxXSxyPW1bMl0scz1tWzNdLHU9bVs0XSwhKF89bVs5XT1tWzldPT09bj9kPzA6ZS5sZW5ndGg6dnIobVs5XS1wLDApKSYmMjQmdCYmKHQmPS0yNSksdCYmMSE9dCliPTg9PXR8fDE2PT10P2Z1bmN0aW9uKGUsdCxyKXt2YXIgbz1QbihlKTtyZXR1cm4gZnVuY3Rpb24gcygpe2Zvcih2YXIgYT1hcmd1bWVudHMubGVuZ3RoLGM9aShhKSxsPWEsdT1vbyhzKTtsLS07KWNbbF09YXJndW1lbnRzW2xdO3ZhciBoPWE8MyYmY1swXSE9PXUmJmNbYS0xXSE9PXU/W106dHIoYyx1KTtyZXR1cm4oYS09aC5sZW5ndGgpPHI/S24oZSx0LGpuLHMucGxhY2Vob2xkZXIsbixjLGgsbixuLHItYSk6Z3QodGhpcyYmdGhpcyE9PW90JiZ0aGlzIGluc3RhbmNlb2Ygcz9vOmUsdGhpcyxjKX19KGUsdCxfKTp0IT1jJiYzMyE9dHx8dS5sZW5ndGg/am4uYXBwbHkobixtKTpmdW5jdGlvbihlLHQscixuKXt2YXIgbz0xJnQscz1QbihlKTtyZXR1cm4gZnVuY3Rpb24gdCgpe2Zvcih2YXIgYT0tMSxjPWFyZ3VtZW50cy5sZW5ndGgsbD0tMSx1PW4ubGVuZ3RoLGg9aSh1K2MpLGY9dGhpcyYmdGhpcyE9PW90JiZ0aGlzIGluc3RhbmNlb2YgdD9zOmU7KytsPHU7KWhbbF09bltsXTtmb3IoO2MtLTspaFtsKytdPWFyZ3VtZW50c1srK2FdO3JldHVybiBndChmLG8/cjp0aGlzLGgpfX0oZSx0LHIscyk7ZWxzZSB2YXIgYj1mdW5jdGlvbihlLHQscil7dmFyIGk9MSZ0LG49UG4oZSk7cmV0dXJuIGZ1bmN0aW9uIHQoKXtyZXR1cm4odGhpcyYmdGhpcyE9PW90JiZ0aGlzIGluc3RhbmNlb2YgdD9uOmUpLmFwcGx5KGk/cjp0aGlzLGFyZ3VtZW50cyl9fShlLHQscik7cmV0dXJuIE9vKCh5P0ppOk1vKShiLG0pLGUsdCl9ZnVuY3Rpb24gWm4oZSx0LHIsaSl7cmV0dXJuIGU9PT1ufHxVcyhlLFJlW3JdKSYmIUJlLmNhbGwoaSxyKT90OmV9ZnVuY3Rpb24gSm4oZSx0LHIsaSxvLHMpe3JldHVybiB0YShlKSYmdGEodCkmJihzLnNldCh0LGUpLEZpKGUsdCxuLEpuLHMpLHMuZGVsZXRlKHQpKSxlfWZ1bmN0aW9uICRuKGUpe3JldHVybiBvYShlKT9uOmV9ZnVuY3Rpb24gUW4oZSx0LHIsaSxvLHMpe3ZhciBhPTEmcixjPWUubGVuZ3RoLGw9dC5sZW5ndGg7aWYoYyE9bCYmIShhJiZsPmMpKXJldHVybiExO3ZhciB1PXMuZ2V0KGUpLGg9cy5nZXQodCk7aWYodSYmaClyZXR1cm4gdT09dCYmaD09ZTt2YXIgZj0tMSxfPSEwLGQ9MiZyP25ldyBWcjpuO2ZvcihzLnNldChlLHQpLHMuc2V0KHQsZSk7KytmPGM7KXt2YXIgcD1lW2ZdLHY9dFtmXTtpZihpKXZhciBnPWE/aSh2LHAsZix0LGUscyk6aShwLHYsZixlLHQscyk7aWYoZyE9PW4pe2lmKGcpY29udGludWU7Xz0hMTticmVha31pZihkKXtpZighTXQodCwoZnVuY3Rpb24oZSx0KXtpZighS3QoZCx0KSYmKHA9PT1lfHxvKHAsZSxyLGkscykpKXJldHVybiBkLnB1c2godCl9KSkpe189ITE7YnJlYWt9fWVsc2UgaWYocCE9PXYmJiFvKHAsdixyLGkscykpe189ITE7YnJlYWt9fXJldHVybiBzLmRlbGV0ZShlKSxzLmRlbGV0ZSh0KSxffWZ1bmN0aW9uIGVvKGUpe3JldHVybiBUbyhFbyhlLG4sVm8pLGUrIiIpfWZ1bmN0aW9uIHRvKGUpe3JldHVybiBDaShlLE9hLHVvKX1mdW5jdGlvbiBybyhlKXtyZXR1cm4gQ2koZSxCYSxobyl9dmFyIGlvPWtyP2Z1bmN0aW9uKGUpe3JldHVybiBrci5nZXQoZSl9OmxjO2Z1bmN0aW9uIG5vKGUpe2Zvcih2YXIgdD1lLm5hbWUrIiIscj1Nclt0XSxpPUJlLmNhbGwoTXIsdCk/ci5sZW5ndGg6MDtpLS07KXt2YXIgbj1yW2ldLG89bi5mdW5jO2lmKG51bGw9PW98fG89PWUpcmV0dXJuIG4ubmFtZX1yZXR1cm4gdH1mdW5jdGlvbiBvbyhlKXtyZXR1cm4oQmUuY2FsbChqciwicGxhY2Vob2xkZXIiKT9qcjplKS5wbGFjZWhvbGRlcn1mdW5jdGlvbiBzbygpe3ZhciBlPWpyLml0ZXJhdGVlfHxvYztyZXR1cm4gZT1lPT09b2M/Qmk6ZSxhcmd1bWVudHMubGVuZ3RoP2UoYXJndW1lbnRzWzBdLGFyZ3VtZW50c1sxXSk6ZX1mdW5jdGlvbiBhbyhlLHQpe3ZhciByLGksbj1lLl9fZGF0YV9fO3JldHVybigic3RyaW5nIj09KGk9dHlwZW9mKHI9dCkpfHwibnVtYmVyIj09aXx8InN5bWJvbCI9PWl8fCJib29sZWFuIj09aT8iX19wcm90b19fIiE9PXI6bnVsbD09PXIpP25bInN0cmluZyI9PXR5cGVvZiB0PyJzdHJpbmciOiJoYXNoIl06bi5tYXB9ZnVuY3Rpb24gY28oZSl7Zm9yKHZhciB0PU9hKGUpLHI9dC5sZW5ndGg7ci0tOyl7dmFyIGk9dFtyXSxuPWVbaV07dFtyXT1baSxuLHdvKG4pXX1yZXR1cm4gdH1mdW5jdGlvbiBsbyhlLHQpe3ZhciByPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIG51bGw9PWU/bjplW3RdfShlLHQpO3JldHVybiBPaShyKT9yOm59dmFyIHVvPWhyP2Z1bmN0aW9uKGUpe3JldHVybiBudWxsPT1lP1tdOihlPUxlKGUpLEN0KGhyKGUpLChmdW5jdGlvbih0KXtyZXR1cm4gZXQuY2FsbChlLHQpfSkpKX06dmMsaG89aHI/ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PVtdO2U7KXh0KHQsdW8oZSkpLGU9VmUoZSk7cmV0dXJuIHR9OnZjLGZvPXdpO2Z1bmN0aW9uIF9vKGUsdCxyKXtmb3IodmFyIGk9LTEsbj0odD1nbih0LGUpKS5sZW5ndGgsbz0hMTsrK2k8bjspe3ZhciBzPWpvKHRbaV0pO2lmKCEobz1udWxsIT1lJiZyKGUscykpKWJyZWFrO2U9ZVtzXX1yZXR1cm4gb3x8KytpIT1uP286ISEobj1udWxsPT1lPzA6ZS5sZW5ndGgpJiZlYShuKSYmZ28ocyxuKSYmKEtzKGUpfHx6cyhlKSl9ZnVuY3Rpb24gcG8oZSl7cmV0dXJuImZ1bmN0aW9uIiE9dHlwZW9mIGUuY29uc3RydWN0b3J8fENvKGUpP3t9OkZyKFZlKGUpKX1mdW5jdGlvbiB2byhlKXtyZXR1cm4gS3MoZSl8fHpzKGUpfHwhIShudCYmZSYmZVtudF0pfWZ1bmN0aW9uIGdvKGUsdCl7dmFyIHI9dHlwZW9mIGU7cmV0dXJuISEodD1udWxsPT10P2g6dCkmJigibnVtYmVyIj09cnx8InN5bWJvbCIhPXImJmdlLnRlc3QoZSkpJiZlPi0xJiZlJTE9PTAmJmU8dH1mdW5jdGlvbiB5byhlLHQscil7aWYoIXRhKHIpKXJldHVybiExO3ZhciBpPXR5cGVvZiB0O3JldHVybiEhKCJudW1iZXIiPT1pP0dzKHIpJiZnbyh0LHIubGVuZ3RoKToic3RyaW5nIj09aSYmdCBpbiByKSYmVXMoclt0XSxlKX1mdW5jdGlvbiBtbyhlLHQpe2lmKEtzKGUpKXJldHVybiExO3ZhciByPXR5cGVvZiBlO3JldHVybiEoIm51bWJlciIhPXImJiJzeW1ib2wiIT1yJiYiYm9vbGVhbiIhPXImJm51bGwhPWUmJiFsYShlKSl8fFEudGVzdChlKXx8ISQudGVzdChlKXx8bnVsbCE9dCYmZSBpbiBMZSh0KX1mdW5jdGlvbiBibyhlKXt2YXIgdD1ubyhlKSxyPWpyW3RdO2lmKCJmdW5jdGlvbiIhPXR5cGVvZiByfHwhKHQgaW4gcXIucHJvdG90eXBlKSlyZXR1cm4hMTtpZihlPT09cilyZXR1cm4hMDt2YXIgaT1pbyhyKTtyZXR1cm4hIWkmJmU9PT1pWzBdfShDciYmZm8obmV3IENyKG5ldyBBcnJheUJ1ZmZlcigxKSkpIT1PfHx3ciYmZm8obmV3IHdyKSE9Q3x8THImJmZvKExyLnJlc29sdmUoKSkhPUV8fEVyJiZmbyhuZXcgRXIpIT1BfHx4ciYmZm8obmV3IHhyKSE9UikmJihmbz1mdW5jdGlvbihlKXt2YXIgdD13aShlKSxyPXQ9PUw/ZS5jb25zdHJ1Y3RvcjpuLGk9cj9GbyhyKToiIjtpZihpKXN3aXRjaChpKXtjYXNlIFJyOnJldHVybiBPO2Nhc2UgVHI6cmV0dXJuIEM7Y2FzZSBPcjpyZXR1cm4gRTtjYXNlIEJyOnJldHVybiBBO2Nhc2UgRHI6cmV0dXJuIFJ9cmV0dXJuIHR9KTt2YXIgU289VGU/JHM6Z2M7ZnVuY3Rpb24gQ28oZSl7dmFyIHQ9ZSYmZS5jb25zdHJ1Y3RvcjtyZXR1cm4gZT09PSgiZnVuY3Rpb24iPT10eXBlb2YgdCYmdC5wcm90b3R5cGV8fFJlKX1mdW5jdGlvbiB3byhlKXtyZXR1cm4gZT09ZSYmIXRhKGUpfWZ1bmN0aW9uIExvKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIpe3JldHVybiBudWxsIT1yJiZyW2VdPT09dCYmKHQhPT1ufHxlIGluIExlKHIpKX19ZnVuY3Rpb24gRW8oZSx0LHIpe3JldHVybiB0PXZyKHQ9PT1uP2UubGVuZ3RoLTE6dCwwKSxmdW5jdGlvbigpe2Zvcih2YXIgbj1hcmd1bWVudHMsbz0tMSxzPXZyKG4ubGVuZ3RoLXQsMCksYT1pKHMpOysrbzxzOylhW29dPW5bdCtvXTtvPS0xO2Zvcih2YXIgYz1pKHQrMSk7KytvPHQ7KWNbb109bltvXTtyZXR1cm4gY1t0XT1yKGEpLGd0KGUsdGhpcyxjKX19ZnVuY3Rpb24geG8oZSx0KXtyZXR1cm4gdC5sZW5ndGg8Mj9lOlNpKGUsZW4odCwwLC0xKSl9ZnVuY3Rpb24gQW8oZSx0KXtmb3IodmFyIHI9ZS5sZW5ndGgsaT1ncih0Lmxlbmd0aCxyKSxvPUFuKGUpO2ktLTspe3ZhciBzPXRbaV07ZVtpXT1nbyhzLHIpP29bc106bn1yZXR1cm4gZX1mdW5jdGlvbiBrbyhlLHQpe2lmKCgiY29uc3RydWN0b3IiIT09dHx8ImZ1bmN0aW9uIiE9dHlwZW9mIGVbdF0pJiYiX19wcm90b19fIiE9dClyZXR1cm4gZVt0XX12YXIgTW89Qm8oSmkpLFJvPWp0fHxmdW5jdGlvbihlLHQpe3JldHVybiBvdC5zZXRUaW1lb3V0KGUsdCl9LFRvPUJvKCRpKTtmdW5jdGlvbiBPbyhlLHQscil7dmFyIGk9dCsiIjtyZXR1cm4gVG8oZSxmdW5jdGlvbihlLHQpe3ZhciByPXQubGVuZ3RoO2lmKCFyKXJldHVybiBlO3ZhciBpPXItMTtyZXR1cm4gdFtpXT0ocj4xPyImICI6IiIpK3RbaV0sdD10LmpvaW4ocj4yPyIsICI6IiAiKSxlLnJlcGxhY2Uob2UsIntcbi8qIFt3cmFwcGVkIHdpdGggIit0KyJdICovXG4iKX0oaSxmdW5jdGlvbihlLHQpe3JldHVybiBtdChkLChmdW5jdGlvbihyKXt2YXIgaT0iXy4iK3JbMF07dCZyWzFdJiYhd3QoZSxpKSYmZS5wdXNoKGkpfSkpLGUuc29ydCgpfShmdW5jdGlvbihlKXt2YXIgdD1lLm1hdGNoKHNlKTtyZXR1cm4gdD90WzFdLnNwbGl0KGFlKTpbXX0oaSkscikpKX1mdW5jdGlvbiBCbyhlKXt2YXIgdD0wLHI9MDtyZXR1cm4gZnVuY3Rpb24oKXt2YXIgaT15cigpLG89MTYtKGktcik7aWYocj1pLG8+MCl7aWYoKyt0Pj04MDApcmV0dXJuIGFyZ3VtZW50c1swXX1lbHNlIHQ9MDtyZXR1cm4gZS5hcHBseShuLGFyZ3VtZW50cyl9fWZ1bmN0aW9uIERvKGUsdCl7dmFyIHI9LTEsaT1lLmxlbmd0aCxvPWktMTtmb3IodD10PT09bj9pOnQ7KytyPHQ7KXt2YXIgcz1LaShyLG8pLGE9ZVtzXTtlW3NdPWVbcl0sZVtyXT1hfXJldHVybiBlLmxlbmd0aD10LGV9dmFyIFBvLElvLEhvPShQbz1QcygoZnVuY3Rpb24oZSl7dmFyIHQ9W107cmV0dXJuIDQ2PT09ZS5jaGFyQ29kZUF0KDApJiZ0LnB1c2goIiIpLGUucmVwbGFjZShlZSwoZnVuY3Rpb24oZSxyLGksbil7dC5wdXNoKGk/bi5yZXBsYWNlKHVlLCIkMSIpOnJ8fGUpfSkpLHR9KSwoZnVuY3Rpb24oZSl7cmV0dXJuIDUwMD09PUlvLnNpemUmJklvLmNsZWFyKCksZX0pKSxJbz1Qby5jYWNoZSxQbyk7ZnVuY3Rpb24gam8oZSl7aWYoInN0cmluZyI9PXR5cGVvZiBlfHxsYShlKSlyZXR1cm4gZTt2YXIgdD1lKyIiO3JldHVybiIwIj09dCYmMS9lPT0tMS8wPyItMCI6dH1mdW5jdGlvbiBGbyhlKXtpZihudWxsIT1lKXt0cnl7cmV0dXJuIE9lLmNhbGwoZSl9Y2F0Y2goZSl7fXRyeXtyZXR1cm4gZSsiIn1jYXRjaChlKXt9fXJldHVybiIifWZ1bmN0aW9uIFdvKGUpe2lmKGUgaW5zdGFuY2VvZiBxcilyZXR1cm4gZS5jbG9uZSgpO3ZhciB0PW5ldyBVcihlLl9fd3JhcHBlZF9fLGUuX19jaGFpbl9fKTtyZXR1cm4gdC5fX2FjdGlvbnNfXz1BbihlLl9fYWN0aW9uc19fKSx0Ll9faW5kZXhfXz1lLl9faW5kZXhfXyx0Ll9fdmFsdWVzX189ZS5fX3ZhbHVlc19fLHR9dmFyIFVvPUdpKChmdW5jdGlvbihlLHQpe3JldHVybiBZcyhlKT9saShlLHBpKHQsMSxZcywhMCkpOltdfSkpLHFvPUdpKChmdW5jdGlvbihlLHQpe3ZhciByPUpvKHQpO3JldHVybiBZcyhyKSYmKHI9biksWXMoZSk/bGkoZSxwaSh0LDEsWXMsITApLHNvKHIsMikpOltdfSkpLE5vPUdpKChmdW5jdGlvbihlLHQpe3ZhciByPUpvKHQpO3JldHVybiBZcyhyKSYmKHI9biksWXMoZSk/bGkoZSxwaSh0LDEsWXMsITApLG4scik6W119KSk7ZnVuY3Rpb24gem8oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtpZighaSlyZXR1cm4tMTt2YXIgbj1udWxsPT1yPzA6cGEocik7cmV0dXJuIG48MCYmKG49dnIoaStuLDApKSxPdChlLHNvKHQsMyksbil9ZnVuY3Rpb24gS28oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtpZighaSlyZXR1cm4tMTt2YXIgbz1pLTE7cmV0dXJuIHIhPT1uJiYobz1wYShyKSxvPXI8MD92cihpK28sMCk6Z3IobyxpLTEpKSxPdChlLHNvKHQsMyksbywhMCl9ZnVuY3Rpb24gVm8oZSl7cmV0dXJuIG51bGwhPWUmJmUubGVuZ3RoP3BpKGUsMSk6W119ZnVuY3Rpb24gR28oZSl7cmV0dXJuIGUmJmUubGVuZ3RoP2VbMF06bn12YXIgWW89R2koKGZ1bmN0aW9uKGUpe3ZhciB0PUV0KGUscG4pO3JldHVybiB0Lmxlbmd0aCYmdFswXT09PWVbMF0/QWkodCk6W119KSksWG89R2koKGZ1bmN0aW9uKGUpe3ZhciB0PUpvKGUpLHI9RXQoZSxwbik7cmV0dXJuIHQ9PT1KbyhyKT90PW46ci5wb3AoKSxyLmxlbmd0aCYmclswXT09PWVbMF0/QWkocixzbyh0LDIpKTpbXX0pKSxabz1HaSgoZnVuY3Rpb24oZSl7dmFyIHQ9Sm8oZSkscj1FdChlLHBuKTtyZXR1cm4odD0iZnVuY3Rpb24iPT10eXBlb2YgdD90Om4pJiZyLnBvcCgpLHIubGVuZ3RoJiZyWzBdPT09ZVswXT9BaShyLG4sdCk6W119KSk7ZnVuY3Rpb24gSm8oZSl7dmFyIHQ9bnVsbD09ZT8wOmUubGVuZ3RoO3JldHVybiB0P2VbdC0xXTpufXZhciAkbz1HaShRbyk7ZnVuY3Rpb24gUW8oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGgmJnQmJnQubGVuZ3RoP05pKGUsdCk6ZX12YXIgZXM9ZW8oKGZ1bmN0aW9uKGUsdCl7dmFyIHI9bnVsbD09ZT8wOmUubGVuZ3RoLGk9bmkoZSx0KTtyZXR1cm4gemkoZSxFdCh0LChmdW5jdGlvbihlKXtyZXR1cm4gZ28oZSxyKT8rZTplfSkpLnNvcnQoTG4pKSxpfSkpO2Z1bmN0aW9uIHRzKGUpe3JldHVybiBudWxsPT1lP2U6U3IuY2FsbChlKX12YXIgcnM9R2koKGZ1bmN0aW9uKGUpe3JldHVybiBjbihwaShlLDEsWXMsITApKX0pKSxpcz1HaSgoZnVuY3Rpb24oZSl7dmFyIHQ9Sm8oZSk7cmV0dXJuIFlzKHQpJiYodD1uKSxjbihwaShlLDEsWXMsITApLHNvKHQsMikpfSkpLG5zPUdpKChmdW5jdGlvbihlKXt2YXIgdD1KbyhlKTtyZXR1cm4gdD0iZnVuY3Rpb24iPT10eXBlb2YgdD90Om4sY24ocGkoZSwxLFlzLCEwKSxuLHQpfSkpO2Z1bmN0aW9uIG9zKGUpe2lmKCFlfHwhZS5sZW5ndGgpcmV0dXJuW107dmFyIHQ9MDtyZXR1cm4gZT1DdChlLChmdW5jdGlvbihlKXtpZihZcyhlKSlyZXR1cm4gdD12cihlLmxlbmd0aCx0KSwhMH0pKSxVdCh0LChmdW5jdGlvbih0KXtyZXR1cm4gRXQoZSxIdCh0KSl9KSl9ZnVuY3Rpb24gc3MoZSx0KXtpZighZXx8IWUubGVuZ3RoKXJldHVybltdO3ZhciByPW9zKGUpO3JldHVybiBudWxsPT10P3I6RXQociwoZnVuY3Rpb24oZSl7cmV0dXJuIGd0KHQsbixlKX0pKX12YXIgYXM9R2koKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIFlzKGUpP2xpKGUsdCk6W119KSksY3M9R2koKGZ1bmN0aW9uKGUpe3JldHVybiBfbihDdChlLFlzKSl9KSksbHM9R2koKGZ1bmN0aW9uKGUpe3ZhciB0PUpvKGUpO3JldHVybiBZcyh0KSYmKHQ9biksX24oQ3QoZSxZcyksc28odCwyKSl9KSksdXM9R2koKGZ1bmN0aW9uKGUpe3ZhciB0PUpvKGUpO3JldHVybiB0PSJmdW5jdGlvbiI9PXR5cGVvZiB0P3Q6bixfbihDdChlLFlzKSxuLHQpfSkpLGhzPUdpKG9zKSxmcz1HaSgoZnVuY3Rpb24oZSl7dmFyIHQ9ZS5sZW5ndGgscj10PjE/ZVt0LTFdOm47cmV0dXJuIHI9ImZ1bmN0aW9uIj09dHlwZW9mIHI/KGUucG9wKCkscik6bixzcyhlLHIpfSkpO2Z1bmN0aW9uIF9zKGUpe3ZhciB0PWpyKGUpO3JldHVybiB0Ll9fY2hhaW5fXz0hMCx0fWZ1bmN0aW9uIGRzKGUsdCl7cmV0dXJuIHQoZSl9dmFyIHBzPWVvKChmdW5jdGlvbihlKXt2YXIgdD1lLmxlbmd0aCxyPXQ/ZVswXTowLGk9dGhpcy5fX3dyYXBwZWRfXyxvPWZ1bmN0aW9uKHQpe3JldHVybiBuaSh0LGUpfTtyZXR1cm4hKHQ+MXx8dGhpcy5fX2FjdGlvbnNfXy5sZW5ndGgpJiZpIGluc3RhbmNlb2YgcXImJmdvKHIpPygoaT1pLnNsaWNlKHIsK3IrKHQ/MTowKSkpLl9fYWN0aW9uc19fLnB1c2goe2Z1bmM6ZHMsYXJnczpbb10sdGhpc0FyZzpufSksbmV3IFVyKGksdGhpcy5fX2NoYWluX18pLnRocnUoKGZ1bmN0aW9uKGUpe3JldHVybiB0JiYhZS5sZW5ndGgmJmUucHVzaChuKSxlfSkpKTp0aGlzLnRocnUobyl9KSksdnM9TW4oKGZ1bmN0aW9uKGUsdCxyKXtCZS5jYWxsKGUscik/KytlW3JdOmlpKGUsciwxKX0pKSxncz1Jbih6bykseXM9SW4oS28pO2Z1bmN0aW9uIG1zKGUsdCl7cmV0dXJuKEtzKGUpP210OnVpKShlLHNvKHQsMykpfWZ1bmN0aW9uIGJzKGUsdCl7cmV0dXJuKEtzKGUpP2J0OmhpKShlLHNvKHQsMykpfXZhciBTcz1NbigoZnVuY3Rpb24oZSx0LHIpe0JlLmNhbGwoZSxyKT9lW3JdLnB1c2godCk6aWkoZSxyLFt0XSl9KSksQ3M9R2koKGZ1bmN0aW9uKGUsdCxyKXt2YXIgbj0tMSxvPSJmdW5jdGlvbiI9PXR5cGVvZiB0LHM9R3MoZSk/aShlLmxlbmd0aCk6W107cmV0dXJuIHVpKGUsKGZ1bmN0aW9uKGUpe3NbKytuXT1vP2d0KHQsZSxyKTpraShlLHQscil9KSksc30pKSx3cz1NbigoZnVuY3Rpb24oZSx0LHIpe2lpKGUscix0KX0pKTtmdW5jdGlvbiBMcyhlLHQpe3JldHVybihLcyhlKT9FdDpJaSkoZSxzbyh0LDMpKX12YXIgRXM9TW4oKGZ1bmN0aW9uKGUsdCxyKXtlW3I/MDoxXS5wdXNoKHQpfSksKGZ1bmN0aW9uKCl7cmV0dXJuW1tdLFtdXX0pKSx4cz1HaSgoZnVuY3Rpb24oZSx0KXtpZihudWxsPT1lKXJldHVybltdO3ZhciByPXQubGVuZ3RoO3JldHVybiByPjEmJnlvKGUsdFswXSx0WzFdKT90PVtdOnI+MiYmeW8odFswXSx0WzFdLHRbMl0pJiYodD1bdFswXV0pLFVpKGUscGkodCwxKSxbXSl9KSksQXM9UnR8fGZ1bmN0aW9uKCl7cmV0dXJuIG90LkRhdGUubm93KCl9O2Z1bmN0aW9uIGtzKGUsdCxyKXtyZXR1cm4gdD1yP246dCx0PWUmJm51bGw9PXQ/ZS5sZW5ndGg6dCxYbihlLGwsbixuLG4sbix0KX1mdW5jdGlvbiBNcyhlLHQpe3ZhciByO2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0KXRocm93IG5ldyBBZShvKTtyZXR1cm4gZT1wYShlKSxmdW5jdGlvbigpe3JldHVybi0tZT4wJiYocj10LmFwcGx5KHRoaXMsYXJndW1lbnRzKSksZTw9MSYmKHQ9bikscn19dmFyIFJzPUdpKChmdW5jdGlvbihlLHQscil7dmFyIGk9MTtpZihyLmxlbmd0aCl7dmFyIG49dHIocixvbyhScykpO2l8PWN9cmV0dXJuIFhuKGUsaSx0LHIsbil9KSksVHM9R2koKGZ1bmN0aW9uKGUsdCxyKXt2YXIgaT0zO2lmKHIubGVuZ3RoKXt2YXIgbj10cihyLG9vKFRzKSk7aXw9Y31yZXR1cm4gWG4odCxpLGUscixuKX0pKTtmdW5jdGlvbiBPcyhlLHQscil7dmFyIGkscyxhLGMsbCx1LGg9MCxmPSExLF89ITEsZD0hMDtpZigiZnVuY3Rpb24iIT10eXBlb2YgZSl0aHJvdyBuZXcgQWUobyk7ZnVuY3Rpb24gcCh0KXt2YXIgcj1pLG89cztyZXR1cm4gaT1zPW4saD10LGM9ZS5hcHBseShvLHIpfWZ1bmN0aW9uIHYoZSl7cmV0dXJuIGg9ZSxsPVJvKHksdCksZj9wKGUpOmN9ZnVuY3Rpb24gZyhlKXt2YXIgcj1lLXU7cmV0dXJuIHU9PT1ufHxyPj10fHxyPDB8fF8mJmUtaD49YX1mdW5jdGlvbiB5KCl7dmFyIGU9QXMoKTtpZihnKGUpKXJldHVybiBtKGUpO2w9Um8oeSxmdW5jdGlvbihlKXt2YXIgcj10LShlLXUpO3JldHVybiBfP2dyKHIsYS0oZS1oKSk6cn0oZSkpfWZ1bmN0aW9uIG0oZSl7cmV0dXJuIGw9bixkJiZpP3AoZSk6KGk9cz1uLGMpfWZ1bmN0aW9uIGIoKXt2YXIgZT1BcygpLHI9ZyhlKTtpZihpPWFyZ3VtZW50cyxzPXRoaXMsdT1lLHIpe2lmKGw9PT1uKXJldHVybiB2KHUpO2lmKF8pcmV0dXJuIGJuKGwpLGw9Um8oeSx0KSxwKHUpfXJldHVybiBsPT09biYmKGw9Um8oeSx0KSksY31yZXR1cm4gdD1nYSh0KXx8MCx0YShyKSYmKGY9ISFyLmxlYWRpbmcsYT0oXz0ibWF4V2FpdCJpbiByKT92cihnYShyLm1heFdhaXQpfHwwLHQpOmEsZD0idHJhaWxpbmciaW4gcj8hIXIudHJhaWxpbmc6ZCksYi5jYW5jZWw9ZnVuY3Rpb24oKXtsIT09biYmYm4obCksaD0wLGk9dT1zPWw9bn0sYi5mbHVzaD1mdW5jdGlvbigpe3JldHVybiBsPT09bj9jOm0oQXMoKSl9LGJ9dmFyIEJzPUdpKChmdW5jdGlvbihlLHQpe3JldHVybiBjaShlLDEsdCl9KSksRHM9R2koKGZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gY2koZSxnYSh0KXx8MCxyKX0pKTtmdW5jdGlvbiBQcyhlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiBlfHxudWxsIT10JiYiZnVuY3Rpb24iIT10eXBlb2YgdCl0aHJvdyBuZXcgQWUobyk7dmFyIHI9ZnVuY3Rpb24oKXt2YXIgaT1hcmd1bWVudHMsbj10P3QuYXBwbHkodGhpcyxpKTppWzBdLG89ci5jYWNoZTtpZihvLmhhcyhuKSlyZXR1cm4gby5nZXQobik7dmFyIHM9ZS5hcHBseSh0aGlzLGkpO3JldHVybiByLmNhY2hlPW8uc2V0KG4scyl8fG8sc307cmV0dXJuIHIuY2FjaGU9bmV3KFBzLkNhY2hlfHxLcikscn1mdW5jdGlvbiBJcyhlKXtpZigiZnVuY3Rpb24iIT10eXBlb2YgZSl0aHJvdyBuZXcgQWUobyk7cmV0dXJuIGZ1bmN0aW9uKCl7dmFyIHQ9YXJndW1lbnRzO3N3aXRjaCh0Lmxlbmd0aCl7Y2FzZSAwOnJldHVybiFlLmNhbGwodGhpcyk7Y2FzZSAxOnJldHVybiFlLmNhbGwodGhpcyx0WzBdKTtjYXNlIDI6cmV0dXJuIWUuY2FsbCh0aGlzLHRbMF0sdFsxXSk7Y2FzZSAzOnJldHVybiFlLmNhbGwodGhpcyx0WzBdLHRbMV0sdFsyXSl9cmV0dXJuIWUuYXBwbHkodGhpcyx0KX19UHMuQ2FjaGU9S3I7dmFyIEhzPXluKChmdW5jdGlvbihlLHQpe3ZhciByPSh0PTE9PXQubGVuZ3RoJiZLcyh0WzBdKT9FdCh0WzBdLE50KHNvKCkpKTpFdChwaSh0LDEpLE50KHNvKCkpKSkubGVuZ3RoO3JldHVybiBHaSgoZnVuY3Rpb24oaSl7Zm9yKHZhciBuPS0xLG89Z3IoaS5sZW5ndGgscik7KytuPG87KWlbbl09dFtuXS5jYWxsKHRoaXMsaVtuXSk7cmV0dXJuIGd0KGUsdGhpcyxpKX0pKX0pKSxqcz1HaSgoZnVuY3Rpb24oZSx0KXt2YXIgcj10cih0LG9vKGpzKSk7cmV0dXJuIFhuKGUsYyxuLHQscil9KSksRnM9R2koKGZ1bmN0aW9uKGUsdCl7dmFyIHI9dHIodCxvbyhGcykpO3JldHVybiBYbihlLDY0LG4sdCxyKX0pKSxXcz1lbygoZnVuY3Rpb24oZSx0KXtyZXR1cm4gWG4oZSwyNTYsbixuLG4sdCl9KSk7ZnVuY3Rpb24gVXMoZSx0KXtyZXR1cm4gZT09PXR8fGUhPWUmJnQhPXR9dmFyIHFzPXpuKExpKSxOcz16bigoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZT49dH0pKSx6cz1NaShmdW5jdGlvbigpe3JldHVybiBhcmd1bWVudHN9KCkpP01pOmZ1bmN0aW9uKGUpe3JldHVybiByYShlKSYmQmUuY2FsbChlLCJjYWxsZWUiKSYmIWV0LmNhbGwoZSwiY2FsbGVlIil9LEtzPWkuaXNBcnJheSxWcz1odD9OdChodCk6ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZ3aShlKT09VH07ZnVuY3Rpb24gR3MoZSl7cmV0dXJuIG51bGwhPWUmJmVhKGUubGVuZ3RoKSYmISRzKGUpfWZ1bmN0aW9uIFlzKGUpe3JldHVybiByYShlKSYmR3MoZSl9dmFyIFhzPWZyfHxnYyxacz1mdD9OdChmdCk6ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZ3aShlKT09eX07ZnVuY3Rpb24gSnMoZSl7aWYoIXJhKGUpKXJldHVybiExO3ZhciB0PXdpKGUpO3JldHVybiB0PT1tfHwiW29iamVjdCBET01FeGNlcHRpb25dIj09dHx8InN0cmluZyI9PXR5cGVvZiBlLm1lc3NhZ2UmJiJzdHJpbmciPT10eXBlb2YgZS5uYW1lJiYhb2EoZSl9ZnVuY3Rpb24gJHMoZSl7aWYoIXRhKGUpKXJldHVybiExO3ZhciB0PXdpKGUpO3JldHVybiB0PT1ifHx0PT1TfHwiW29iamVjdCBBc3luY0Z1bmN0aW9uXSI9PXR8fCJbb2JqZWN0IFByb3h5XSI9PXR9ZnVuY3Rpb24gUXMoZSl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlJiZlPT1wYShlKX1mdW5jdGlvbiBlYShlKXtyZXR1cm4ibnVtYmVyIj09dHlwZW9mIGUmJmU+LTEmJmUlMT09MCYmZTw9aH1mdW5jdGlvbiB0YShlKXt2YXIgdD10eXBlb2YgZTtyZXR1cm4gbnVsbCE9ZSYmKCJvYmplY3QiPT10fHwiZnVuY3Rpb24iPT10KX1mdW5jdGlvbiByYShlKXtyZXR1cm4gbnVsbCE9ZSYmIm9iamVjdCI9PXR5cGVvZiBlfXZhciBpYT1fdD9OdChfdCk6ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZmbyhlKT09Q307ZnVuY3Rpb24gbmEoZSl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlfHxyYShlKSYmd2koZSk9PXd9ZnVuY3Rpb24gb2EoZSl7aWYoIXJhKGUpfHx3aShlKSE9TClyZXR1cm4hMTt2YXIgdD1WZShlKTtpZihudWxsPT09dClyZXR1cm4hMDt2YXIgcj1CZS5jYWxsKHQsImNvbnN0cnVjdG9yIikmJnQuY29uc3RydWN0b3I7cmV0dXJuImZ1bmN0aW9uIj09dHlwZW9mIHImJnIgaW5zdGFuY2VvZiByJiZPZS5jYWxsKHIpPT1IZX12YXIgc2E9ZHQ/TnQoZHQpOmZ1bmN0aW9uKGUpe3JldHVybiByYShlKSYmd2koZSk9PXh9LGFhPXB0P050KHB0KTpmdW5jdGlvbihlKXtyZXR1cm4gcmEoZSkmJmZvKGUpPT1BfTtmdW5jdGlvbiBjYShlKXtyZXR1cm4ic3RyaW5nIj09dHlwZW9mIGV8fCFLcyhlKSYmcmEoZSkmJndpKGUpPT1rfWZ1bmN0aW9uIGxhKGUpe3JldHVybiJzeW1ib2wiPT10eXBlb2YgZXx8cmEoZSkmJndpKGUpPT1NfXZhciB1YT12dD9OdCh2dCk6ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZlYShlLmxlbmd0aCkmJiEhJGVbd2koZSldfSxoYT16bihQaSksZmE9em4oKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGU8PXR9KSk7ZnVuY3Rpb24gX2EoZSl7aWYoIWUpcmV0dXJuW107aWYoR3MoZSkpcmV0dXJuIGNhKGUpP29yKGUpOkFuKGUpO2lmKHN0JiZlW3N0XSlyZXR1cm4gZnVuY3Rpb24oZSl7Zm9yKHZhciB0LHI9W107ISh0PWUubmV4dCgpKS5kb25lOylyLnB1c2godC52YWx1ZSk7cmV0dXJuIHJ9KGVbc3RdKCkpO3ZhciB0PWZvKGUpO3JldHVybih0PT1DP1F0OnQ9PUE/cnI6VWEpKGUpfWZ1bmN0aW9uIGRhKGUpe3JldHVybiBlPyhlPWdhKGUpKT09PXV8fGU9PT0tMS8wPzE3OTc2OTMxMzQ4NjIzMTU3ZTI5MiooZTwwPy0xOjEpOmU9PWU/ZTowOjA9PT1lP2U6MH1mdW5jdGlvbiBwYShlKXt2YXIgdD1kYShlKSxyPXQlMTtyZXR1cm4gdD09dD9yP3Qtcjp0OjB9ZnVuY3Rpb24gdmEoZSl7cmV0dXJuIGU/b2kocGEoZSksMCxfKTowfWZ1bmN0aW9uIGdhKGUpe2lmKCJudW1iZXIiPT10eXBlb2YgZSlyZXR1cm4gZTtpZihsYShlKSlyZXR1cm4gZjtpZih0YShlKSl7dmFyIHQ9ImZ1bmN0aW9uIj09dHlwZW9mIGUudmFsdWVPZj9lLnZhbHVlT2YoKTplO2U9dGEodCk/dCsiIjp0fWlmKCJzdHJpbmciIT10eXBlb2YgZSlyZXR1cm4gMD09PWU/ZTorZTtlPXF0KGUpO3ZhciByPWRlLnRlc3QoZSk7cmV0dXJuIHJ8fHZlLnRlc3QoZSk/cnQoZS5zbGljZSgyKSxyPzI6OCk6X2UudGVzdChlKT9mOitlfWZ1bmN0aW9uIHlhKGUpe3JldHVybiBrbihlLEJhKGUpKX1mdW5jdGlvbiBtYShlKXtyZXR1cm4gbnVsbD09ZT8iIjphbihlKX12YXIgYmE9Um4oKGZ1bmN0aW9uKGUsdCl7aWYoQ28odCl8fEdzKHQpKWtuKHQsT2EodCksZSk7ZWxzZSBmb3IodmFyIHIgaW4gdClCZS5jYWxsKHQscikmJlFyKGUscix0W3JdKX0pKSxTYT1SbigoZnVuY3Rpb24oZSx0KXtrbih0LEJhKHQpLGUpfSkpLENhPVJuKChmdW5jdGlvbihlLHQscixpKXtrbih0LEJhKHQpLGUsaSl9KSksd2E9Um4oKGZ1bmN0aW9uKGUsdCxyLGkpe2tuKHQsT2EodCksZSxpKX0pKSxMYT1lbyhuaSksRWE9R2koKGZ1bmN0aW9uKGUsdCl7ZT1MZShlKTt2YXIgcj0tMSxpPXQubGVuZ3RoLG89aT4yP3RbMl06bjtmb3IobyYmeW8odFswXSx0WzFdLG8pJiYoaT0xKTsrK3I8aTspZm9yKHZhciBzPXRbcl0sYT1CYShzKSxjPS0xLGw9YS5sZW5ndGg7KytjPGw7KXt2YXIgdT1hW2NdLGg9ZVt1XTsoaD09PW58fFVzKGgsUmVbdV0pJiYhQmUuY2FsbChlLHUpKSYmKGVbdV09c1t1XSl9cmV0dXJuIGV9KSkseGE9R2koKGZ1bmN0aW9uKGUpe3JldHVybiBlLnB1c2gobixKbiksZ3QoUGEsbixlKX0pKTtmdW5jdGlvbiBBYShlLHQscil7dmFyIGk9bnVsbD09ZT9uOlNpKGUsdCk7cmV0dXJuIGk9PT1uP3I6aX1mdW5jdGlvbiBrYShlLHQpe3JldHVybiBudWxsIT1lJiZfbyhlLHQseGkpfXZhciBNYT1GbigoZnVuY3Rpb24oZSx0LHIpe251bGwhPXQmJiJmdW5jdGlvbiIhPXR5cGVvZiB0LnRvU3RyaW5nJiYodD1JZS5jYWxsKHQpKSxlW3RdPXJ9KSx0YyhuYykpLFJhPUZuKChmdW5jdGlvbihlLHQscil7bnVsbCE9dCYmImZ1bmN0aW9uIiE9dHlwZW9mIHQudG9TdHJpbmcmJih0PUllLmNhbGwodCkpLEJlLmNhbGwoZSx0KT9lW3RdLnB1c2gocik6ZVt0XT1bcl19KSxzbyksVGE9R2koa2kpO2Z1bmN0aW9uIE9hKGUpe3JldHVybiBHcyhlKT9ZcihlKTpEaShlKX1mdW5jdGlvbiBCYShlKXtyZXR1cm4gR3MoZSk/WXIoZSwhMCk6ZnVuY3Rpb24oZSl7aWYoIXRhKGUpKXJldHVybiBmdW5jdGlvbihlKXt2YXIgdD1bXTtpZihudWxsIT1lKWZvcih2YXIgciBpbiBMZShlKSl0LnB1c2gocik7cmV0dXJuIHR9KGUpO3ZhciB0PUNvKGUpLHI9W107Zm9yKHZhciBpIGluIGUpKCJjb25zdHJ1Y3RvciIhPWl8fCF0JiZCZS5jYWxsKGUsaSkpJiZyLnB1c2goaSk7cmV0dXJuIHJ9KGUpfXZhciBEYT1SbigoZnVuY3Rpb24oZSx0LHIpe0ZpKGUsdCxyKX0pKSxQYT1SbigoZnVuY3Rpb24oZSx0LHIsaSl7RmkoZSx0LHIsaSl9KSksSWE9ZW8oKGZ1bmN0aW9uKGUsdCl7dmFyIHI9e307aWYobnVsbD09ZSlyZXR1cm4gcjt2YXIgaT0hMTt0PUV0KHQsKGZ1bmN0aW9uKHQpe3JldHVybiB0PWduKHQsZSksaXx8KGk9dC5sZW5ndGg+MSksdH0pKSxrbihlLHJvKGUpLHIpLGkmJihyPXNpKHIsNywkbikpO2Zvcih2YXIgbj10Lmxlbmd0aDtuLS07KWxuKHIsdFtuXSk7cmV0dXJuIHJ9KSksSGE9ZW8oKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIG51bGw9PWU/e306ZnVuY3Rpb24oZSx0KXtyZXR1cm4gcWkoZSx0LChmdW5jdGlvbih0LHIpe3JldHVybiBrYShlLHIpfSkpfShlLHQpfSkpO2Z1bmN0aW9uIGphKGUsdCl7aWYobnVsbD09ZSlyZXR1cm57fTt2YXIgcj1FdChybyhlKSwoZnVuY3Rpb24oZSl7cmV0dXJuW2VdfSkpO3JldHVybiB0PXNvKHQpLHFpKGUsciwoZnVuY3Rpb24oZSxyKXtyZXR1cm4gdChlLHJbMF0pfSkpfXZhciBGYT1ZbihPYSksV2E9WW4oQmEpO2Z1bmN0aW9uIFVhKGUpe3JldHVybiBudWxsPT1lP1tdOnp0KGUsT2EoZSkpfXZhciBxYT1EbigoZnVuY3Rpb24oZSx0LHIpe3JldHVybiB0PXQudG9Mb3dlckNhc2UoKSxlKyhyP05hKHQpOnQpfSkpO2Z1bmN0aW9uIE5hKGUpe3JldHVybiBKYShtYShlKS50b0xvd2VyQ2FzZSgpKX1mdW5jdGlvbiB6YShlKXtyZXR1cm4oZT1tYShlKSkmJmUucmVwbGFjZSh5ZSxYdCkucmVwbGFjZShLZSwiIil9dmFyIEthPURuKChmdW5jdGlvbihlLHQscil7cmV0dXJuIGUrKHI/Ii0iOiIiKSt0LnRvTG93ZXJDYXNlKCl9KSksVmE9RG4oKGZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gZSsocj8iICI6IiIpK3QudG9Mb3dlckNhc2UoKX0pKSxHYT1CbigidG9Mb3dlckNhc2UiKSxZYT1EbigoZnVuY3Rpb24oZSx0LHIpe3JldHVybiBlKyhyPyJfIjoiIikrdC50b0xvd2VyQ2FzZSgpfSkpLFhhPURuKChmdW5jdGlvbihlLHQscil7cmV0dXJuIGUrKHI/IiAiOiIiKStKYSh0KX0pKSxaYT1EbigoZnVuY3Rpb24oZSx0LHIpe3JldHVybiBlKyhyPyIgIjoiIikrdC50b1VwcGVyQ2FzZSgpfSkpLEphPUJuKCJ0b1VwcGVyQ2FzZSIpO2Z1bmN0aW9uICRhKGUsdCxyKXtyZXR1cm4gZT1tYShlKSwodD1yP246dCk9PT1uP2Z1bmN0aW9uKGUpe3JldHVybiBYZS50ZXN0KGUpfShlKT9mdW5jdGlvbihlKXtyZXR1cm4gZS5tYXRjaChHZSl8fFtdfShlKTpmdW5jdGlvbihlKXtyZXR1cm4gZS5tYXRjaChjZSl8fFtdfShlKTplLm1hdGNoKHQpfHxbXX12YXIgUWE9R2koKGZ1bmN0aW9uKGUsdCl7dHJ5e3JldHVybiBndChlLG4sdCl9Y2F0Y2goZSl7cmV0dXJuIEpzKGUpP2U6bmV3IFNlKGUpfX0pKSxlYz1lbygoZnVuY3Rpb24oZSx0KXtyZXR1cm4gbXQodCwoZnVuY3Rpb24odCl7dD1qbyh0KSxpaShlLHQsUnMoZVt0XSxlKSl9KSksZX0pKTtmdW5jdGlvbiB0YyhlKXtyZXR1cm4gZnVuY3Rpb24oKXtyZXR1cm4gZX19dmFyIHJjPUhuKCksaWM9SG4oITApO2Z1bmN0aW9uIG5jKGUpe3JldHVybiBlfWZ1bmN0aW9uIG9jKGUpe3JldHVybiBCaSgiZnVuY3Rpb24iPT10eXBlb2YgZT9lOnNpKGUsMSkpfXZhciBzYz1HaSgoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocil7cmV0dXJuIGtpKHIsZSx0KX19KSksYWM9R2koKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIpe3JldHVybiBraShlLHIsdCl9fSkpO2Z1bmN0aW9uIGNjKGUsdCxyKXt2YXIgaT1PYSh0KSxuPWJpKHQsaSk7bnVsbCE9cnx8dGEodCkmJihuLmxlbmd0aHx8IWkubGVuZ3RoKXx8KHI9dCx0PWUsZT10aGlzLG49YmkodCxPYSh0KSkpO3ZhciBvPSEodGEocikmJiJjaGFpbiJpbiByJiYhci5jaGFpbikscz0kcyhlKTtyZXR1cm4gbXQobiwoZnVuY3Rpb24ocil7dmFyIGk9dFtyXTtlW3JdPWkscyYmKGUucHJvdG90eXBlW3JdPWZ1bmN0aW9uKCl7dmFyIHQ9dGhpcy5fX2NoYWluX187aWYob3x8dCl7dmFyIHI9ZSh0aGlzLl9fd3JhcHBlZF9fKSxuPXIuX19hY3Rpb25zX189QW4odGhpcy5fX2FjdGlvbnNfXyk7cmV0dXJuIG4ucHVzaCh7ZnVuYzppLGFyZ3M6YXJndW1lbnRzLHRoaXNBcmc6ZX0pLHIuX19jaGFpbl9fPXQscn1yZXR1cm4gaS5hcHBseShlLHh0KFt0aGlzLnZhbHVlKCldLGFyZ3VtZW50cykpfSl9KSksZX1mdW5jdGlvbiBsYygpe312YXIgdWM9VW4oRXQpLGhjPVVuKFN0KSxmYz1VbihNdCk7ZnVuY3Rpb24gX2MoZSl7cmV0dXJuIG1vKGUpP0h0KGpvKGUpKTpmdW5jdGlvbihlKXtyZXR1cm4gZnVuY3Rpb24odCl7cmV0dXJuIFNpKHQsZSl9fShlKX12YXIgZGM9Tm4oKSxwYz1ObighMCk7ZnVuY3Rpb24gdmMoKXtyZXR1cm5bXX1mdW5jdGlvbiBnYygpe3JldHVybiExfXZhciB5YyxtYz1XbigoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSt0fSksMCksYmM9Vm4oImNlaWwiKSxTYz1XbigoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZS90fSksMSksQ2M9Vm4oImZsb29yIiksd2M9V24oKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUqdH0pLDEpLExjPVZuKCJyb3VuZCIpLEVjPVduKChmdW5jdGlvbihlLHQpe3JldHVybiBlLXR9KSwwKTtyZXR1cm4ganIuYWZ0ZXI9ZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgdCl0aHJvdyBuZXcgQWUobyk7cmV0dXJuIGU9cGEoZSksZnVuY3Rpb24oKXtpZigtLWU8MSlyZXR1cm4gdC5hcHBseSh0aGlzLGFyZ3VtZW50cyl9fSxqci5hcnk9a3MsanIuYXNzaWduPWJhLGpyLmFzc2lnbkluPVNhLGpyLmFzc2lnbkluV2l0aD1DYSxqci5hc3NpZ25XaXRoPXdhLGpyLmF0PUxhLGpyLmJlZm9yZT1Ncyxqci5iaW5kPVJzLGpyLmJpbmRBbGw9ZWMsanIuYmluZEtleT1Ucyxqci5jYXN0QXJyYXk9ZnVuY3Rpb24oKXtpZighYXJndW1lbnRzLmxlbmd0aClyZXR1cm5bXTt2YXIgZT1hcmd1bWVudHNbMF07cmV0dXJuIEtzKGUpP2U6W2VdfSxqci5jaGFpbj1fcyxqci5jaHVuaz1mdW5jdGlvbihlLHQscil7dD0ocj95byhlLHQscik6dD09PW4pPzE6dnIocGEodCksMCk7dmFyIG89bnVsbD09ZT8wOmUubGVuZ3RoO2lmKCFvfHx0PDEpcmV0dXJuW107Zm9yKHZhciBzPTAsYT0wLGM9aShscihvL3QpKTtzPG87KWNbYSsrXT1lbihlLHMscys9dCk7cmV0dXJuIGN9LGpyLmNvbXBhY3Q9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PS0xLHI9bnVsbD09ZT8wOmUubGVuZ3RoLGk9MCxuPVtdOysrdDxyOyl7dmFyIG89ZVt0XTtvJiYobltpKytdPW8pfXJldHVybiBufSxqci5jb25jYXQ9ZnVuY3Rpb24oKXt2YXIgZT1hcmd1bWVudHMubGVuZ3RoO2lmKCFlKXJldHVybltdO2Zvcih2YXIgdD1pKGUtMSkscj1hcmd1bWVudHNbMF0sbj1lO24tLTspdFtuLTFdPWFyZ3VtZW50c1tuXTtyZXR1cm4geHQoS3Mocik/QW4ocik6W3JdLHBpKHQsMSkpfSxqci5jb25kPWZ1bmN0aW9uKGUpe3ZhciB0PW51bGw9PWU/MDplLmxlbmd0aCxyPXNvKCk7cmV0dXJuIGU9dD9FdChlLChmdW5jdGlvbihlKXtpZigiZnVuY3Rpb24iIT10eXBlb2YgZVsxXSl0aHJvdyBuZXcgQWUobyk7cmV0dXJuW3IoZVswXSksZVsxXV19KSk6W10sR2koKGZ1bmN0aW9uKHIpe2Zvcih2YXIgaT0tMTsrK2k8dDspe3ZhciBuPWVbaV07aWYoZ3QoblswXSx0aGlzLHIpKXJldHVybiBndChuWzFdLHRoaXMscil9fSkpfSxqci5jb25mb3Jtcz1mdW5jdGlvbihlKXtyZXR1cm4gZnVuY3Rpb24oZSl7dmFyIHQ9T2EoZSk7cmV0dXJuIGZ1bmN0aW9uKHIpe3JldHVybiBhaShyLGUsdCl9fShzaShlLDEpKX0sanIuY29uc3RhbnQ9dGMsanIuY291bnRCeT12cyxqci5jcmVhdGU9ZnVuY3Rpb24oZSx0KXt2YXIgcj1GcihlKTtyZXR1cm4gbnVsbD09dD9yOnJpKHIsdCl9LGpyLmN1cnJ5PWZ1bmN0aW9uIGUodCxyLGkpe3ZhciBvPVhuKHQsOCxuLG4sbixuLG4scj1pP246cik7cmV0dXJuIG8ucGxhY2Vob2xkZXI9ZS5wbGFjZWhvbGRlcixvfSxqci5jdXJyeVJpZ2h0PWZ1bmN0aW9uIGUodCxyLGkpe3ZhciBvPVhuKHQsMTYsbixuLG4sbixuLHI9aT9uOnIpO3JldHVybiBvLnBsYWNlaG9sZGVyPWUucGxhY2Vob2xkZXIsb30sanIuZGVib3VuY2U9T3MsanIuZGVmYXVsdHM9RWEsanIuZGVmYXVsdHNEZWVwPXhhLGpyLmRlZmVyPUJzLGpyLmRlbGF5PURzLGpyLmRpZmZlcmVuY2U9VW8sanIuZGlmZmVyZW5jZUJ5PXFvLGpyLmRpZmZlcmVuY2VXaXRoPU5vLGpyLmRyb3A9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtyZXR1cm4gaT9lbihlLCh0PXJ8fHQ9PT1uPzE6cGEodCkpPDA/MDp0LGkpOltdfSxqci5kcm9wUmlnaHQ9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtyZXR1cm4gaT9lbihlLDAsKHQ9aS0odD1yfHx0PT09bj8xOnBhKHQpKSk8MD8wOnQpOltdfSxqci5kcm9wUmlnaHRXaGlsZT1mdW5jdGlvbihlLHQpe3JldHVybiBlJiZlLmxlbmd0aD9obihlLHNvKHQsMyksITAsITApOltdfSxqci5kcm9wV2hpbGU9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/aG4oZSxzbyh0LDMpLCEwKTpbXX0sanIuZmlsbD1mdW5jdGlvbihlLHQscixpKXt2YXIgbz1udWxsPT1lPzA6ZS5sZW5ndGg7cmV0dXJuIG8/KHImJiJudW1iZXIiIT10eXBlb2YgciYmeW8oZSx0LHIpJiYocj0wLGk9byksZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG89ZS5sZW5ndGg7Zm9yKChyPXBhKHIpKTwwJiYocj0tcj5vPzA6bytyKSwoaT1pPT09bnx8aT5vP286cGEoaSkpPDAmJihpKz1vKSxpPXI+aT8wOnZhKGkpO3I8aTspZVtyKytdPXQ7cmV0dXJuIGV9KGUsdCxyLGkpKTpbXX0sanIuZmlsdGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuKEtzKGUpP0N0OmRpKShlLHNvKHQsMykpfSxqci5mbGF0TWFwPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHBpKExzKGUsdCksMSl9LGpyLmZsYXRNYXBEZWVwPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHBpKExzKGUsdCksdSl9LGpyLmZsYXRNYXBEZXB0aD1mdW5jdGlvbihlLHQscil7cmV0dXJuIHI9cj09PW4/MTpwYShyKSxwaShMcyhlLHQpLHIpfSxqci5mbGF0dGVuPVZvLGpyLmZsYXR0ZW5EZWVwPWZ1bmN0aW9uKGUpe3JldHVybiBudWxsIT1lJiZlLmxlbmd0aD9waShlLHUpOltdfSxqci5mbGF0dGVuRGVwdGg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gbnVsbCE9ZSYmZS5sZW5ndGg/cGkoZSx0PXQ9PT1uPzE6cGEodCkpOltdfSxqci5mbGlwPWZ1bmN0aW9uKGUpe3JldHVybiBYbihlLDUxMil9LGpyLmZsb3c9cmMsanIuZmxvd1JpZ2h0PWljLGpyLmZyb21QYWlycz1mdW5jdGlvbihlKXtmb3IodmFyIHQ9LTEscj1udWxsPT1lPzA6ZS5sZW5ndGgsaT17fTsrK3Q8cjspe3ZhciBuPWVbdF07aVtuWzBdXT1uWzFdfXJldHVybiBpfSxqci5mdW5jdGlvbnM9ZnVuY3Rpb24oZSl7cmV0dXJuIG51bGw9PWU/W106YmkoZSxPYShlKSl9LGpyLmZ1bmN0aW9uc0luPWZ1bmN0aW9uKGUpe3JldHVybiBudWxsPT1lP1tdOmJpKGUsQmEoZSkpfSxqci5ncm91cEJ5PVNzLGpyLmluaXRpYWw9ZnVuY3Rpb24oZSl7cmV0dXJuIG51bGwhPWUmJmUubGVuZ3RoP2VuKGUsMCwtMSk6W119LGpyLmludGVyc2VjdGlvbj1Zbyxqci5pbnRlcnNlY3Rpb25CeT1Ybyxqci5pbnRlcnNlY3Rpb25XaXRoPVpvLGpyLmludmVydD1NYSxqci5pbnZlcnRCeT1SYSxqci5pbnZva2VNYXA9Q3MsanIuaXRlcmF0ZWU9b2MsanIua2V5Qnk9d3MsanIua2V5cz1PYSxqci5rZXlzSW49QmEsanIubWFwPUxzLGpyLm1hcEtleXM9ZnVuY3Rpb24oZSx0KXt2YXIgcj17fTtyZXR1cm4gdD1zbyh0LDMpLHlpKGUsKGZ1bmN0aW9uKGUsaSxuKXtpaShyLHQoZSxpLG4pLGUpfSkpLHJ9LGpyLm1hcFZhbHVlcz1mdW5jdGlvbihlLHQpe3ZhciByPXt9O3JldHVybiB0PXNvKHQsMykseWkoZSwoZnVuY3Rpb24oZSxpLG4pe2lpKHIsaSx0KGUsaSxuKSl9KSkscn0sanIubWF0Y2hlcz1mdW5jdGlvbihlKXtyZXR1cm4gSGkoc2koZSwxKSl9LGpyLm1hdGNoZXNQcm9wZXJ0eT1mdW5jdGlvbihlLHQpe3JldHVybiBqaShlLHNpKHQsMSkpfSxqci5tZW1vaXplPVBzLGpyLm1lcmdlPURhLGpyLm1lcmdlV2l0aD1QYSxqci5tZXRob2Q9c2MsanIubWV0aG9kT2Y9YWMsanIubWl4aW49Y2MsanIubmVnYXRlPUlzLGpyLm50aEFyZz1mdW5jdGlvbihlKXtyZXR1cm4gZT1wYShlKSxHaSgoZnVuY3Rpb24odCl7cmV0dXJuIFdpKHQsZSl9KSl9LGpyLm9taXQ9SWEsanIub21pdEJ5PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGphKGUsSXMoc28odCkpKX0sanIub25jZT1mdW5jdGlvbihlKXtyZXR1cm4gTXMoMixlKX0sanIub3JkZXJCeT1mdW5jdGlvbihlLHQscixpKXtyZXR1cm4gbnVsbD09ZT9bXTooS3ModCl8fCh0PW51bGw9PXQ/W106W3RdKSxLcyhyPWk/bjpyKXx8KHI9bnVsbD09cj9bXTpbcl0pLFVpKGUsdCxyKSl9LGpyLm92ZXI9dWMsanIub3ZlckFyZ3M9SHMsanIub3ZlckV2ZXJ5PWhjLGpyLm92ZXJTb21lPWZjLGpyLnBhcnRpYWw9anMsanIucGFydGlhbFJpZ2h0PUZzLGpyLnBhcnRpdGlvbj1Fcyxqci5waWNrPUhhLGpyLnBpY2tCeT1qYSxqci5wcm9wZXJ0eT1fYyxqci5wcm9wZXJ0eU9mPWZ1bmN0aW9uKGUpe3JldHVybiBmdW5jdGlvbih0KXtyZXR1cm4gbnVsbD09ZT9uOlNpKGUsdCl9fSxqci5wdWxsPSRvLGpyLnB1bGxBbGw9UW8sanIucHVsbEFsbEJ5PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gZSYmZS5sZW5ndGgmJnQmJnQubGVuZ3RoP05pKGUsdCxzbyhyLDIpKTplfSxqci5wdWxsQWxsV2l0aD1mdW5jdGlvbihlLHQscil7cmV0dXJuIGUmJmUubGVuZ3RoJiZ0JiZ0Lmxlbmd0aD9OaShlLHQsbixyKTplfSxqci5wdWxsQXQ9ZXMsanIucmFuZ2U9ZGMsanIucmFuZ2VSaWdodD1wYyxqci5yZWFyZz1Xcyxqci5yZWplY3Q9ZnVuY3Rpb24oZSx0KXtyZXR1cm4oS3MoZSk/Q3Q6ZGkpKGUsSXMoc28odCwzKSkpfSxqci5yZW1vdmU9ZnVuY3Rpb24oZSx0KXt2YXIgcj1bXTtpZighZXx8IWUubGVuZ3RoKXJldHVybiByO3ZhciBpPS0xLG49W10sbz1lLmxlbmd0aDtmb3IodD1zbyh0LDMpOysraTxvOyl7dmFyIHM9ZVtpXTt0KHMsaSxlKSYmKHIucHVzaChzKSxuLnB1c2goaSkpfXJldHVybiB6aShlLG4pLHJ9LGpyLnJlc3Q9ZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgZSl0aHJvdyBuZXcgQWUobyk7cmV0dXJuIEdpKGUsdD10PT09bj90OnBhKHQpKX0sanIucmV2ZXJzZT10cyxqci5zYW1wbGVTaXplPWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gdD0ocj95byhlLHQscik6dD09PW4pPzE6cGEodCksKEtzKGUpP1pyOlhpKShlLHQpfSxqci5zZXQ9ZnVuY3Rpb24oZSx0LHIpe3JldHVybiBudWxsPT1lP2U6WmkoZSx0LHIpfSxqci5zZXRXaXRoPWZ1bmN0aW9uKGUsdCxyLGkpe3JldHVybiBpPSJmdW5jdGlvbiI9PXR5cGVvZiBpP2k6bixudWxsPT1lP2U6WmkoZSx0LHIsaSl9LGpyLnNodWZmbGU9ZnVuY3Rpb24oZSl7cmV0dXJuKEtzKGUpP0pyOlFpKShlKX0sanIuc2xpY2U9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtyZXR1cm4gaT8ociYmIm51bWJlciIhPXR5cGVvZiByJiZ5byhlLHQscik/KHQ9MCxyPWkpOih0PW51bGw9PXQ/MDpwYSh0KSxyPXI9PT1uP2k6cGEocikpLGVuKGUsdCxyKSk6W119LGpyLnNvcnRCeT14cyxqci5zb3J0ZWRVbmlxPWZ1bmN0aW9uKGUpe3JldHVybiBlJiZlLmxlbmd0aD9vbihlKTpbXX0sanIuc29ydGVkVW5pcUJ5PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUmJmUubGVuZ3RoP29uKGUsc28odCwyKSk6W119LGpyLnNwbGl0PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gciYmIm51bWJlciIhPXR5cGVvZiByJiZ5byhlLHQscikmJih0PXI9biksKHI9cj09PW4/XzpyPj4+MCk/KGU9bWEoZSkpJiYoInN0cmluZyI9PXR5cGVvZiB0fHxudWxsIT10JiYhc2EodCkpJiYhKHQ9YW4odCkpJiYkdChlKT9tbihvcihlKSwwLHIpOmUuc3BsaXQodCxyKTpbXX0sanIuc3ByZWFkPWZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIGUpdGhyb3cgbmV3IEFlKG8pO3JldHVybiB0PW51bGw9PXQ/MDp2cihwYSh0KSwwKSxHaSgoZnVuY3Rpb24ocil7dmFyIGk9clt0XSxuPW1uKHIsMCx0KTtyZXR1cm4gaSYmeHQobixpKSxndChlLHRoaXMsbil9KSl9LGpyLnRhaWw9ZnVuY3Rpb24oZSl7dmFyIHQ9bnVsbD09ZT8wOmUubGVuZ3RoO3JldHVybiB0P2VuKGUsMSx0KTpbXX0sanIudGFrZT1mdW5jdGlvbihlLHQscil7cmV0dXJuIGUmJmUubGVuZ3RoP2VuKGUsMCwodD1yfHx0PT09bj8xOnBhKHQpKTwwPzA6dCk6W119LGpyLnRha2VSaWdodD1mdW5jdGlvbihlLHQscil7dmFyIGk9bnVsbD09ZT8wOmUubGVuZ3RoO3JldHVybiBpP2VuKGUsKHQ9aS0odD1yfHx0PT09bj8xOnBhKHQpKSk8MD8wOnQsaSk6W119LGpyLnRha2VSaWdodFdoaWxlPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUmJmUubGVuZ3RoP2huKGUsc28odCwzKSwhMSwhMCk6W119LGpyLnRha2VXaGlsZT1mdW5jdGlvbihlLHQpe3JldHVybiBlJiZlLmxlbmd0aD9obihlLHNvKHQsMykpOltdfSxqci50YXA9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdChlKSxlfSxqci50aHJvdHRsZT1mdW5jdGlvbihlLHQscil7dmFyIGk9ITAsbj0hMDtpZigiZnVuY3Rpb24iIT10eXBlb2YgZSl0aHJvdyBuZXcgQWUobyk7cmV0dXJuIHRhKHIpJiYoaT0ibGVhZGluZyJpbiByPyEhci5sZWFkaW5nOmksbj0idHJhaWxpbmciaW4gcj8hIXIudHJhaWxpbmc6biksT3MoZSx0LHtsZWFkaW5nOmksbWF4V2FpdDp0LHRyYWlsaW5nOm59KX0sanIudGhydT1kcyxqci50b0FycmF5PV9hLGpyLnRvUGFpcnM9RmEsanIudG9QYWlyc0luPVdhLGpyLnRvUGF0aD1mdW5jdGlvbihlKXtyZXR1cm4gS3MoZSk/RXQoZSxqbyk6bGEoZSk/W2VdOkFuKEhvKG1hKGUpKSl9LGpyLnRvUGxhaW5PYmplY3Q9eWEsanIudHJhbnNmb3JtPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1LcyhlKSxuPWl8fFhzKGUpfHx1YShlKTtpZih0PXNvKHQsNCksbnVsbD09cil7dmFyIG89ZSYmZS5jb25zdHJ1Y3RvcjtyPW4/aT9uZXcgbzpbXTp0YShlKSYmJHMobyk/RnIoVmUoZSkpOnt9fXJldHVybihuP210OnlpKShlLChmdW5jdGlvbihlLGksbil7cmV0dXJuIHQocixlLGksbil9KSkscn0sanIudW5hcnk9ZnVuY3Rpb24oZSl7cmV0dXJuIGtzKGUsMSl9LGpyLnVuaW9uPXJzLGpyLnVuaW9uQnk9aXMsanIudW5pb25XaXRoPW5zLGpyLnVuaXE9ZnVuY3Rpb24oZSl7cmV0dXJuIGUmJmUubGVuZ3RoP2NuKGUpOltdfSxqci51bmlxQnk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/Y24oZSxzbyh0LDIpKTpbXX0sanIudW5pcVdpdGg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdD0iZnVuY3Rpb24iPT10eXBlb2YgdD90Om4sZSYmZS5sZW5ndGg/Y24oZSxuLHQpOltdfSxqci51bnNldD1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT1lfHxsbihlLHQpfSxqci51bnppcD1vcyxqci51bnppcFdpdGg9c3MsanIudXBkYXRlPWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gbnVsbD09ZT9lOnVuKGUsdCx2bihyKSl9LGpyLnVwZGF0ZVdpdGg9ZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuIGk9ImZ1bmN0aW9uIj09dHlwZW9mIGk/aTpuLG51bGw9PWU/ZTp1bihlLHQsdm4ociksaSl9LGpyLnZhbHVlcz1VYSxqci52YWx1ZXNJbj1mdW5jdGlvbihlKXtyZXR1cm4gbnVsbD09ZT9bXTp6dChlLEJhKGUpKX0sanIud2l0aG91dD1hcyxqci53b3Jkcz0kYSxqci53cmFwPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGpzKHZuKHQpLGUpfSxqci54b3I9Y3MsanIueG9yQnk9bHMsanIueG9yV2l0aD11cyxqci56aXA9aHMsanIuemlwT2JqZWN0PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGRuKGV8fFtdLHR8fFtdLFFyKX0sanIuemlwT2JqZWN0RGVlcD1mdW5jdGlvbihlLHQpe3JldHVybiBkbihlfHxbXSx0fHxbXSxaaSl9LGpyLnppcFdpdGg9ZnMsanIuZW50cmllcz1GYSxqci5lbnRyaWVzSW49V2EsanIuZXh0ZW5kPVNhLGpyLmV4dGVuZFdpdGg9Q2EsY2MoanIsanIpLGpyLmFkZD1tYyxqci5hdHRlbXB0PVFhLGpyLmNhbWVsQ2FzZT1xYSxqci5jYXBpdGFsaXplPU5hLGpyLmNlaWw9YmMsanIuY2xhbXA9ZnVuY3Rpb24oZSx0LHIpe3JldHVybiByPT09biYmKHI9dCx0PW4pLHIhPT1uJiYocj0ocj1nYShyKSk9PXI/cjowKSx0IT09biYmKHQ9KHQ9Z2EodCkpPT10P3Q6MCksb2koZ2EoZSksdCxyKX0sanIuY2xvbmU9ZnVuY3Rpb24oZSl7cmV0dXJuIHNpKGUsNCl9LGpyLmNsb25lRGVlcD1mdW5jdGlvbihlKXtyZXR1cm4gc2koZSw1KX0sanIuY2xvbmVEZWVwV2l0aD1mdW5jdGlvbihlLHQpe3JldHVybiBzaShlLDUsdD0iZnVuY3Rpb24iPT10eXBlb2YgdD90Om4pfSxqci5jbG9uZVdpdGg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gc2koZSw0LHQ9ImZ1bmN0aW9uIj09dHlwZW9mIHQ/dDpuKX0sanIuY29uZm9ybXNUbz1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT10fHxhaShlLHQsT2EodCkpfSxqci5kZWJ1cnI9emEsanIuZGVmYXVsdFRvPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIG51bGw9PWV8fGUhPWU/dDplfSxqci5kaXZpZGU9U2MsanIuZW5kc1dpdGg9ZnVuY3Rpb24oZSx0LHIpe2U9bWEoZSksdD1hbih0KTt2YXIgaT1lLmxlbmd0aCxvPXI9cj09PW4/aTpvaShwYShyKSwwLGkpO3JldHVybihyLT10Lmxlbmd0aCk+PTAmJmUuc2xpY2UocixvKT09dH0sanIuZXE9VXMsanIuZXNjYXBlPWZ1bmN0aW9uKGUpe3JldHVybihlPW1hKGUpKSYmWS50ZXN0KGUpP2UucmVwbGFjZShWLFp0KTplfSxqci5lc2NhcGVSZWdFeHA9ZnVuY3Rpb24oZSl7cmV0dXJuKGU9bWEoZSkpJiZyZS50ZXN0KGUpP2UucmVwbGFjZSh0ZSwiXFwkJiIpOmV9LGpyLmV2ZXJ5PWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1LcyhlKT9TdDpmaTtyZXR1cm4gciYmeW8oZSx0LHIpJiYodD1uKSxpKGUsc28odCwzKSl9LGpyLmZpbmQ9Z3MsanIuZmluZEluZGV4PXpvLGpyLmZpbmRLZXk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gVHQoZSxzbyh0LDMpLHlpKX0sanIuZmluZExhc3Q9eXMsanIuZmluZExhc3RJbmRleD1Lbyxqci5maW5kTGFzdEtleT1mdW5jdGlvbihlLHQpe3JldHVybiBUdChlLHNvKHQsMyksbWkpfSxqci5mbG9vcj1DYyxqci5mb3JFYWNoPW1zLGpyLmZvckVhY2hSaWdodD1icyxqci5mb3JJbj1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT1lP2U6dmkoZSxzbyh0LDMpLEJhKX0sanIuZm9ySW5SaWdodD1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT1lP2U6Z2koZSxzbyh0LDMpLEJhKX0sanIuZm9yT3duPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUmJnlpKGUsc28odCwzKSl9LGpyLmZvck93blJpZ2h0PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUmJm1pKGUsc28odCwzKSl9LGpyLmdldD1BYSxqci5ndD1xcyxqci5ndGU9TnMsanIuaGFzPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIG51bGwhPWUmJl9vKGUsdCxFaSl9LGpyLmhhc0luPWthLGpyLmhlYWQ9R28sanIuaWRlbnRpdHk9bmMsanIuaW5jbHVkZXM9ZnVuY3Rpb24oZSx0LHIsaSl7ZT1HcyhlKT9lOlVhKGUpLHI9ciYmIWk/cGEocik6MDt2YXIgbj1lLmxlbmd0aDtyZXR1cm4gcjwwJiYocj12cihuK3IsMCkpLGNhKGUpP3I8PW4mJmUuaW5kZXhPZih0LHIpPi0xOiEhbiYmQnQoZSx0LHIpPi0xfSxqci5pbmRleE9mPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1udWxsPT1lPzA6ZS5sZW5ndGg7aWYoIWkpcmV0dXJuLTE7dmFyIG49bnVsbD09cj8wOnBhKHIpO3JldHVybiBuPDAmJihuPXZyKGkrbiwwKSksQnQoZSx0LG4pfSxqci5pblJhbmdlPWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gdD1kYSh0KSxyPT09bj8ocj10LHQ9MCk6cj1kYShyKSxmdW5jdGlvbihlLHQscil7cmV0dXJuIGU+PWdyKHQscikmJmU8dnIodCxyKX0oZT1nYShlKSx0LHIpfSxqci5pbnZva2U9VGEsanIuaXNBcmd1bWVudHM9enMsanIuaXNBcnJheT1Lcyxqci5pc0FycmF5QnVmZmVyPVZzLGpyLmlzQXJyYXlMaWtlPUdzLGpyLmlzQXJyYXlMaWtlT2JqZWN0PVlzLGpyLmlzQm9vbGVhbj1mdW5jdGlvbihlKXtyZXR1cm4hMD09PWV8fCExPT09ZXx8cmEoZSkmJndpKGUpPT1nfSxqci5pc0J1ZmZlcj1Ycyxqci5pc0RhdGU9WnMsanIuaXNFbGVtZW50PWZ1bmN0aW9uKGUpe3JldHVybiByYShlKSYmMT09PWUubm9kZVR5cGUmJiFvYShlKX0sanIuaXNFbXB0eT1mdW5jdGlvbihlKXtpZihudWxsPT1lKXJldHVybiEwO2lmKEdzKGUpJiYoS3MoZSl8fCJzdHJpbmciPT10eXBlb2YgZXx8ImZ1bmN0aW9uIj09dHlwZW9mIGUuc3BsaWNlfHxYcyhlKXx8dWEoZSl8fHpzKGUpKSlyZXR1cm4hZS5sZW5ndGg7dmFyIHQ9Zm8oZSk7aWYodD09Q3x8dD09QSlyZXR1cm4hZS5zaXplO2lmKENvKGUpKXJldHVybiFEaShlKS5sZW5ndGg7Zm9yKHZhciByIGluIGUpaWYoQmUuY2FsbChlLHIpKXJldHVybiExO3JldHVybiEwfSxqci5pc0VxdWFsPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIFJpKGUsdCl9LGpyLmlzRXF1YWxXaXRoPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT0ocj0iZnVuY3Rpb24iPT10eXBlb2Ygcj9yOm4pP3IoZSx0KTpuO3JldHVybiBpPT09bj9SaShlLHQsbixyKTohIWl9LGpyLmlzRXJyb3I9SnMsanIuaXNGaW5pdGU9ZnVuY3Rpb24oZSl7cmV0dXJuIm51bWJlciI9PXR5cGVvZiBlJiZfcihlKX0sanIuaXNGdW5jdGlvbj0kcyxqci5pc0ludGVnZXI9UXMsanIuaXNMZW5ndGg9ZWEsanIuaXNNYXA9aWEsanIuaXNNYXRjaD1mdW5jdGlvbihlLHQpe3JldHVybiBlPT09dHx8VGkoZSx0LGNvKHQpKX0sanIuaXNNYXRjaFdpdGg9ZnVuY3Rpb24oZSx0LHIpe3JldHVybiByPSJmdW5jdGlvbiI9PXR5cGVvZiByP3I6bixUaShlLHQsY28odCkscil9LGpyLmlzTmFOPWZ1bmN0aW9uKGUpe3JldHVybiBuYShlKSYmZSE9K2V9LGpyLmlzTmF0aXZlPWZ1bmN0aW9uKGUpe2lmKFNvKGUpKXRocm93IG5ldyBTZSgiVW5zdXBwb3J0ZWQgY29yZS1qcyB1c2UuIFRyeSBodHRwczovL25wbXMuaW8vc2VhcmNoP3E9cG9ueWZpbGwuIik7cmV0dXJuIE9pKGUpfSxqci5pc05pbD1mdW5jdGlvbihlKXtyZXR1cm4gbnVsbD09ZX0sanIuaXNOdWxsPWZ1bmN0aW9uKGUpe3JldHVybiBudWxsPT09ZX0sanIuaXNOdW1iZXI9bmEsanIuaXNPYmplY3Q9dGEsanIuaXNPYmplY3RMaWtlPXJhLGpyLmlzUGxhaW5PYmplY3Q9b2EsanIuaXNSZWdFeHA9c2EsanIuaXNTYWZlSW50ZWdlcj1mdW5jdGlvbihlKXtyZXR1cm4gUXMoZSkmJmU+PS05MDA3MTk5MjU0NzQwOTkxJiZlPD1ofSxqci5pc1NldD1hYSxqci5pc1N0cmluZz1jYSxqci5pc1N5bWJvbD1sYSxqci5pc1R5cGVkQXJyYXk9dWEsanIuaXNVbmRlZmluZWQ9ZnVuY3Rpb24oZSl7cmV0dXJuIGU9PT1ufSxqci5pc1dlYWtNYXA9ZnVuY3Rpb24oZSl7cmV0dXJuIHJhKGUpJiZmbyhlKT09Un0sanIuaXNXZWFrU2V0PWZ1bmN0aW9uKGUpe3JldHVybiByYShlKSYmIltvYmplY3QgV2Vha1NldF0iPT13aShlKX0sanIuam9pbj1mdW5jdGlvbihlLHQpe3JldHVybiBudWxsPT1lPyIiOmRyLmNhbGwoZSx0KX0sanIua2ViYWJDYXNlPUthLGpyLmxhc3Q9Sm8sanIubGFzdEluZGV4T2Y9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW51bGw9PWU/MDplLmxlbmd0aDtpZighaSlyZXR1cm4tMTt2YXIgbz1pO3JldHVybiByIT09biYmKG89KG89cGEocikpPDA/dnIoaStvLDApOmdyKG8saS0xKSksdD09dD9mdW5jdGlvbihlLHQscil7Zm9yKHZhciBpPXIrMTtpLS07KWlmKGVbaV09PT10KXJldHVybiBpO3JldHVybiBpfShlLHQsbyk6T3QoZSxQdCxvLCEwKX0sanIubG93ZXJDYXNlPVZhLGpyLmxvd2VyRmlyc3Q9R2EsanIubHQ9aGEsanIubHRlPWZhLGpyLm1heD1mdW5jdGlvbihlKXtyZXR1cm4gZSYmZS5sZW5ndGg/X2koZSxuYyxMaSk6bn0sanIubWF4Qnk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/X2koZSxzbyh0LDIpLExpKTpufSxqci5tZWFuPWZ1bmN0aW9uKGUpe3JldHVybiBJdChlLG5jKX0sanIubWVhbkJ5PWZ1bmN0aW9uKGUsdCl7cmV0dXJuIEl0KGUsc28odCwyKSl9LGpyLm1pbj1mdW5jdGlvbihlKXtyZXR1cm4gZSYmZS5sZW5ndGg/X2koZSxuYyxQaSk6bn0sanIubWluQnk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/X2koZSxzbyh0LDIpLFBpKTpufSxqci5zdHViQXJyYXk9dmMsanIuc3R1YkZhbHNlPWdjLGpyLnN0dWJPYmplY3Q9ZnVuY3Rpb24oKXtyZXR1cm57fX0sanIuc3R1YlN0cmluZz1mdW5jdGlvbigpe3JldHVybiIifSxqci5zdHViVHJ1ZT1mdW5jdGlvbigpe3JldHVybiEwfSxqci5tdWx0aXBseT13Yyxqci5udGg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/V2koZSxwYSh0KSk6bn0sanIubm9Db25mbGljdD1mdW5jdGlvbigpe3JldHVybiBvdC5fPT09dGhpcyYmKG90Ll89amUpLHRoaXN9LGpyLm5vb3A9bGMsanIubm93PUFzLGpyLnBhZD1mdW5jdGlvbihlLHQscil7ZT1tYShlKTt2YXIgaT0odD1wYSh0KSk/bnIoZSk6MDtpZighdHx8aT49dClyZXR1cm4gZTt2YXIgbj0odC1pKS8yO3JldHVybiBxbih1cihuKSxyKStlK3FuKGxyKG4pLHIpfSxqci5wYWRFbmQ9ZnVuY3Rpb24oZSx0LHIpe2U9bWEoZSk7dmFyIGk9KHQ9cGEodCkpP25yKGUpOjA7cmV0dXJuIHQmJmk8dD9lK3FuKHQtaSxyKTplfSxqci5wYWRTdGFydD1mdW5jdGlvbihlLHQscil7ZT1tYShlKTt2YXIgaT0odD1wYSh0KSk/bnIoZSk6MDtyZXR1cm4gdCYmaTx0P3FuKHQtaSxyKStlOmV9LGpyLnBhcnNlSW50PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gcnx8bnVsbD09dD90PTA6dCYmKHQ9K3QpLG1yKG1hKGUpLnJlcGxhY2UoaWUsIiIpLHR8fDApfSxqci5yYW5kb209ZnVuY3Rpb24oZSx0LHIpe2lmKHImJiJib29sZWFuIiE9dHlwZW9mIHImJnlvKGUsdCxyKSYmKHQ9cj1uKSxyPT09biYmKCJib29sZWFuIj09dHlwZW9mIHQ/KHI9dCx0PW4pOiJib29sZWFuIj09dHlwZW9mIGUmJihyPWUsZT1uKSksZT09PW4mJnQ9PT1uPyhlPTAsdD0xKTooZT1kYShlKSx0PT09bj8odD1lLGU9MCk6dD1kYSh0KSksZT50KXt2YXIgaT1lO2U9dCx0PWl9aWYocnx8ZSUxfHx0JTEpe3ZhciBvPWJyKCk7cmV0dXJuIGdyKGUrbyoodC1lK3R0KCIxZS0iKygobysiIikubGVuZ3RoLTEpKSksdCl9cmV0dXJuIEtpKGUsdCl9LGpyLnJlZHVjZT1mdW5jdGlvbihlLHQscil7dmFyIGk9S3MoZSk/QXQ6RnQsbj1hcmd1bWVudHMubGVuZ3RoPDM7cmV0dXJuIGkoZSxzbyh0LDQpLHIsbix1aSl9LGpyLnJlZHVjZVJpZ2h0PWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1LcyhlKT9rdDpGdCxuPWFyZ3VtZW50cy5sZW5ndGg8MztyZXR1cm4gaShlLHNvKHQsNCkscixuLGhpKX0sanIucmVwZWF0PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gdD0ocj95byhlLHQscik6dD09PW4pPzE6cGEodCksVmkobWEoZSksdCl9LGpyLnJlcGxhY2U9ZnVuY3Rpb24oKXt2YXIgZT1hcmd1bWVudHMsdD1tYShlWzBdKTtyZXR1cm4gZS5sZW5ndGg8Mz90OnQucmVwbGFjZShlWzFdLGVbMl0pfSxqci5yZXN1bHQ9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPS0xLG89KHQ9Z24odCxlKSkubGVuZ3RoO2ZvcihvfHwobz0xLGU9bik7KytpPG87KXt2YXIgcz1udWxsPT1lP246ZVtqbyh0W2ldKV07cz09PW4mJihpPW8scz1yKSxlPSRzKHMpP3MuY2FsbChlKTpzfXJldHVybiBlfSxqci5yb3VuZD1MYyxqci5ydW5JbkNvbnRleHQ9ZSxqci5zYW1wbGU9ZnVuY3Rpb24oZSl7cmV0dXJuKEtzKGUpP1hyOllpKShlKX0sanIuc2l6ZT1mdW5jdGlvbihlKXtpZihudWxsPT1lKXJldHVybiAwO2lmKEdzKGUpKXJldHVybiBjYShlKT9ucihlKTplLmxlbmd0aDt2YXIgdD1mbyhlKTtyZXR1cm4gdD09Q3x8dD09QT9lLnNpemU6RGkoZSkubGVuZ3RofSxqci5zbmFrZUNhc2U9WWEsanIuc29tZT1mdW5jdGlvbihlLHQscil7dmFyIGk9S3MoZSk/TXQ6dG47cmV0dXJuIHImJnlvKGUsdCxyKSYmKHQ9biksaShlLHNvKHQsMykpfSxqci5zb3J0ZWRJbmRleD1mdW5jdGlvbihlLHQpe3JldHVybiBybihlLHQpfSxqci5zb3J0ZWRJbmRleEJ5PWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gbm4oZSx0LHNvKHIsMikpfSxqci5zb3J0ZWRJbmRleE9mPWZ1bmN0aW9uKGUsdCl7dmFyIHI9bnVsbD09ZT8wOmUubGVuZ3RoO2lmKHIpe3ZhciBpPXJuKGUsdCk7aWYoaTxyJiZVcyhlW2ldLHQpKXJldHVybiBpfXJldHVybi0xfSxqci5zb3J0ZWRMYXN0SW5kZXg9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gcm4oZSx0LCEwKX0sanIuc29ydGVkTGFzdEluZGV4Qnk9ZnVuY3Rpb24oZSx0LHIpe3JldHVybiBubihlLHQsc28ociwyKSwhMCl9LGpyLnNvcnRlZExhc3RJbmRleE9mPWZ1bmN0aW9uKGUsdCl7aWYobnVsbCE9ZSYmZS5sZW5ndGgpe3ZhciByPXJuKGUsdCwhMCktMTtpZihVcyhlW3JdLHQpKXJldHVybiByfXJldHVybi0xfSxqci5zdGFydENhc2U9WGEsanIuc3RhcnRzV2l0aD1mdW5jdGlvbihlLHQscil7cmV0dXJuIGU9bWEoZSkscj1udWxsPT1yPzA6b2kocGEociksMCxlLmxlbmd0aCksdD1hbih0KSxlLnNsaWNlKHIscit0Lmxlbmd0aCk9PXR9LGpyLnN1YnRyYWN0PUVjLGpyLnN1bT1mdW5jdGlvbihlKXtyZXR1cm4gZSYmZS5sZW5ndGg/V3QoZSxuYyk6MH0sanIuc3VtQnk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSYmZS5sZW5ndGg/V3QoZSxzbyh0LDIpKTowfSxqci50ZW1wbGF0ZT1mdW5jdGlvbihlLHQscil7dmFyIGk9anIudGVtcGxhdGVTZXR0aW5ncztyJiZ5byhlLHQscikmJih0PW4pLGU9bWEoZSksdD1DYSh7fSx0LGksWm4pO3ZhciBvLHMsYT1DYSh7fSx0LmltcG9ydHMsaS5pbXBvcnRzLFpuKSxjPU9hKGEpLGw9enQoYSxjKSx1PTAsaD10LmludGVycG9sYXRlfHxtZSxmPSJfX3AgKz0gJyIsXz1FZSgodC5lc2NhcGV8fG1lKS5zb3VyY2UrInwiK2guc291cmNlKyJ8IisoaD09PUo/aGU6bWUpLnNvdXJjZSsifCIrKHQuZXZhbHVhdGV8fG1lKS5zb3VyY2UrInwkIiwiZyIpLGQ9Ii8vIyBzb3VyY2VVUkw9IisoQmUuY2FsbCh0LCJzb3VyY2VVUkwiKT8odC5zb3VyY2VVUkwrIiIpLnJlcGxhY2UoL1xzL2csIiAiKToibG9kYXNoLnRlbXBsYXRlU291cmNlc1siKyArK0plKyJdIikrIlxuIjtlLnJlcGxhY2UoXywoZnVuY3Rpb24odCxyLGksbixhLGMpe3JldHVybiBpfHwoaT1uKSxmKz1lLnNsaWNlKHUsYykucmVwbGFjZShiZSxKdCksciYmKG89ITAsZis9IicgK1xuX19lKCIrcisiKSArXG4nIiksYSYmKHM9ITAsZis9Iic7XG4iK2ErIjtcbl9fcCArPSAnIiksaSYmKGYrPSInICtcbigoX190ID0gKCIraSsiKSkgPT0gbnVsbCA/ICcnIDogX190KSArXG4nIiksdT1jK3QubGVuZ3RoLHR9KSksZis9Iic7XG4iO3ZhciBwPUJlLmNhbGwodCwidmFyaWFibGUiKSYmdC52YXJpYWJsZTtpZihwKXtpZihsZS50ZXN0KHApKXRocm93IG5ldyBTZSgiSW52YWxpZCBgdmFyaWFibGVgIG9wdGlvbiBwYXNzZWQgaW50byBgXy50ZW1wbGF0ZWAiKX1lbHNlIGY9IndpdGggKG9iaikge1xuIitmKyJcbn1cbiI7Zj0ocz9mLnJlcGxhY2UocSwiIik6ZikucmVwbGFjZShOLCIkMSIpLnJlcGxhY2UoeiwiJDE7IiksZj0iZnVuY3Rpb24oIisocHx8Im9iaiIpKyIpIHtcbiIrKHA/IiI6Im9iaiB8fCAob2JqID0ge30pO1xuIikrInZhciBfX3QsIF9fcCA9ICcnIisobz8iLCBfX2UgPSBfLmVzY2FwZSI6IiIpKyhzPyIsIF9faiA9IEFycmF5LnByb3RvdHlwZS5qb2luO1xuZnVuY3Rpb24gcHJpbnQoKSB7IF9fcCArPSBfX2ouY2FsbChhcmd1bWVudHMsICcnKSB9XG4iOiI7XG4iKStmKyJyZXR1cm4gX19wXG59Ijt2YXIgdj1RYSgoZnVuY3Rpb24oKXtyZXR1cm4gQ2UoYyxkKyJyZXR1cm4gIitmKS5hcHBseShuLGwpfSkpO2lmKHYuc291cmNlPWYsSnModikpdGhyb3cgdjtyZXR1cm4gdn0sanIudGltZXM9ZnVuY3Rpb24oZSx0KXtpZigoZT1wYShlKSk8MXx8ZT5oKXJldHVybltdO3ZhciByPV8saT1ncihlLF8pO3Q9c28odCksZS09Xztmb3IodmFyIG49VXQoaSx0KTsrK3I8ZTspdChyKTtyZXR1cm4gbn0sanIudG9GaW5pdGU9ZGEsanIudG9JbnRlZ2VyPXBhLGpyLnRvTGVuZ3RoPXZhLGpyLnRvTG93ZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIG1hKGUpLnRvTG93ZXJDYXNlKCl9LGpyLnRvTnVtYmVyPWdhLGpyLnRvU2FmZUludGVnZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIGU/b2kocGEoZSksLTkwMDcxOTkyNTQ3NDA5OTEsaCk6MD09PWU/ZTowfSxqci50b1N0cmluZz1tYSxqci50b1VwcGVyPWZ1bmN0aW9uKGUpe3JldHVybiBtYShlKS50b1VwcGVyQ2FzZSgpfSxqci50cmltPWZ1bmN0aW9uKGUsdCxyKXtpZigoZT1tYShlKSkmJihyfHx0PT09bikpcmV0dXJuIHF0KGUpO2lmKCFlfHwhKHQ9YW4odCkpKXJldHVybiBlO3ZhciBpPW9yKGUpLG89b3IodCk7cmV0dXJuIG1uKGksVnQoaSxvKSxHdChpLG8pKzEpLmpvaW4oIiIpfSxqci50cmltRW5kPWZ1bmN0aW9uKGUsdCxyKXtpZigoZT1tYShlKSkmJihyfHx0PT09bikpcmV0dXJuIGUuc2xpY2UoMCxzcihlKSsxKTtpZighZXx8ISh0PWFuKHQpKSlyZXR1cm4gZTt2YXIgaT1vcihlKTtyZXR1cm4gbW4oaSwwLEd0KGksb3IodCkpKzEpLmpvaW4oIiIpfSxqci50cmltU3RhcnQ9ZnVuY3Rpb24oZSx0LHIpe2lmKChlPW1hKGUpKSYmKHJ8fHQ9PT1uKSlyZXR1cm4gZS5yZXBsYWNlKGllLCIiKTtpZighZXx8ISh0PWFuKHQpKSlyZXR1cm4gZTt2YXIgaT1vcihlKTtyZXR1cm4gbW4oaSxWdChpLG9yKHQpKSkuam9pbigiIil9LGpyLnRydW5jYXRlPWZ1bmN0aW9uKGUsdCl7dmFyIHI9MzAsaT0iLi4uIjtpZih0YSh0KSl7dmFyIG89InNlcGFyYXRvciJpbiB0P3Quc2VwYXJhdG9yOm87cj0ibGVuZ3RoImluIHQ/cGEodC5sZW5ndGgpOnIsaT0ib21pc3Npb24iaW4gdD9hbih0Lm9taXNzaW9uKTppfXZhciBzPShlPW1hKGUpKS5sZW5ndGg7aWYoJHQoZSkpe3ZhciBhPW9yKGUpO3M9YS5sZW5ndGh9aWYocj49cylyZXR1cm4gZTt2YXIgYz1yLW5yKGkpO2lmKGM8MSlyZXR1cm4gaTt2YXIgbD1hP21uKGEsMCxjKS5qb2luKCIiKTplLnNsaWNlKDAsYyk7aWYobz09PW4pcmV0dXJuIGwraTtpZihhJiYoYys9bC5sZW5ndGgtYyksc2Eobykpe2lmKGUuc2xpY2UoYykuc2VhcmNoKG8pKXt2YXIgdSxoPWw7Zm9yKG8uZ2xvYmFsfHwobz1FZShvLnNvdXJjZSxtYShmZS5leGVjKG8pKSsiZyIpKSxvLmxhc3RJbmRleD0wO3U9by5leGVjKGgpOyl2YXIgZj11LmluZGV4O2w9bC5zbGljZSgwLGY9PT1uP2M6Zil9fWVsc2UgaWYoZS5pbmRleE9mKGFuKG8pLGMpIT1jKXt2YXIgXz1sLmxhc3RJbmRleE9mKG8pO18+LTEmJihsPWwuc2xpY2UoMCxfKSl9cmV0dXJuIGwraX0sanIudW5lc2NhcGU9ZnVuY3Rpb24oZSl7cmV0dXJuKGU9bWEoZSkpJiZHLnRlc3QoZSk/ZS5yZXBsYWNlKEssYXIpOmV9LGpyLnVuaXF1ZUlkPWZ1bmN0aW9uKGUpe3ZhciB0PSsrRGU7cmV0dXJuIG1hKGUpK3R9LGpyLnVwcGVyQ2FzZT1aYSxqci51cHBlckZpcnN0PUphLGpyLmVhY2g9bXMsanIuZWFjaFJpZ2h0PWJzLGpyLmZpcnN0PUdvLGNjKGpyLCh5Yz17fSx5aShqciwoZnVuY3Rpb24oZSx0KXtCZS5jYWxsKGpyLnByb3RvdHlwZSx0KXx8KHljW3RdPWUpfSkpLHljKSx7Y2hhaW46ITF9KSxqci5WRVJTSU9OPSI0LjE3LjIxIixtdChbImJpbmQiLCJiaW5kS2V5IiwiY3VycnkiLCJjdXJyeVJpZ2h0IiwicGFydGlhbCIsInBhcnRpYWxSaWdodCJdLChmdW5jdGlvbihlKXtqcltlXS5wbGFjZWhvbGRlcj1qcn0pKSxtdChbImRyb3AiLCJ0YWtlIl0sKGZ1bmN0aW9uKGUsdCl7cXIucHJvdG90eXBlW2VdPWZ1bmN0aW9uKHIpe3I9cj09PW4/MTp2cihwYShyKSwwKTt2YXIgaT10aGlzLl9fZmlsdGVyZWRfXyYmIXQ/bmV3IHFyKHRoaXMpOnRoaXMuY2xvbmUoKTtyZXR1cm4gaS5fX2ZpbHRlcmVkX18/aS5fX3Rha2VDb3VudF9fPWdyKHIsaS5fX3Rha2VDb3VudF9fKTppLl9fdmlld3NfXy5wdXNoKHtzaXplOmdyKHIsXyksdHlwZTplKyhpLl9fZGlyX188MD8iUmlnaHQiOiIiKX0pLGl9LHFyLnByb3RvdHlwZVtlKyJSaWdodCJdPWZ1bmN0aW9uKHQpe3JldHVybiB0aGlzLnJldmVyc2UoKVtlXSh0KS5yZXZlcnNlKCl9fSkpLG10KFsiZmlsdGVyIiwibWFwIiwidGFrZVdoaWxlIl0sKGZ1bmN0aW9uKGUsdCl7dmFyIHI9dCsxLGk9MT09cnx8Mz09cjtxci5wcm90b3R5cGVbZV09ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5jbG9uZSgpO3JldHVybiB0Ll9faXRlcmF0ZWVzX18ucHVzaCh7aXRlcmF0ZWU6c28oZSwzKSx0eXBlOnJ9KSx0Ll9fZmlsdGVyZWRfXz10Ll9fZmlsdGVyZWRfX3x8aSx0fX0pKSxtdChbImhlYWQiLCJsYXN0Il0sKGZ1bmN0aW9uKGUsdCl7dmFyIHI9InRha2UiKyh0PyJSaWdodCI6IiIpO3FyLnByb3RvdHlwZVtlXT1mdW5jdGlvbigpe3JldHVybiB0aGlzW3JdKDEpLnZhbHVlKClbMF19fSkpLG10KFsiaW5pdGlhbCIsInRhaWwiXSwoZnVuY3Rpb24oZSx0KXt2YXIgcj0iZHJvcCIrKHQ/IiI6IlJpZ2h0Iik7cXIucHJvdG90eXBlW2VdPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX19maWx0ZXJlZF9fP25ldyBxcih0aGlzKTp0aGlzW3JdKDEpfX0pKSxxci5wcm90b3R5cGUuY29tcGFjdD1mdW5jdGlvbigpe3JldHVybiB0aGlzLmZpbHRlcihuYyl9LHFyLnByb3RvdHlwZS5maW5kPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLmZpbHRlcihlKS5oZWFkKCl9LHFyLnByb3RvdHlwZS5maW5kTGFzdD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5yZXZlcnNlKCkuZmluZChlKX0scXIucHJvdG90eXBlLmludm9rZU1hcD1HaSgoZnVuY3Rpb24oZSx0KXtyZXR1cm4iZnVuY3Rpb24iPT10eXBlb2YgZT9uZXcgcXIodGhpcyk6dGhpcy5tYXAoKGZ1bmN0aW9uKHIpe3JldHVybiBraShyLGUsdCl9KSl9KSkscXIucHJvdG90eXBlLnJlamVjdD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5maWx0ZXIoSXMoc28oZSkpKX0scXIucHJvdG90eXBlLnNsaWNlPWZ1bmN0aW9uKGUsdCl7ZT1wYShlKTt2YXIgcj10aGlzO3JldHVybiByLl9fZmlsdGVyZWRfXyYmKGU+MHx8dDwwKT9uZXcgcXIocik6KGU8MD9yPXIudGFrZVJpZ2h0KC1lKTplJiYocj1yLmRyb3AoZSkpLHQhPT1uJiYocj0odD1wYSh0KSk8MD9yLmRyb3BSaWdodCgtdCk6ci50YWtlKHQtZSkpLHIpfSxxci5wcm90b3R5cGUudGFrZVJpZ2h0V2hpbGU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMucmV2ZXJzZSgpLnRha2VXaGlsZShlKS5yZXZlcnNlKCl9LHFyLnByb3RvdHlwZS50b0FycmF5PWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMudGFrZShfKX0seWkocXIucHJvdG90eXBlLChmdW5jdGlvbihlLHQpe3ZhciByPS9eKD86ZmlsdGVyfGZpbmR8bWFwfHJlamVjdCl8V2hpbGUkLy50ZXN0KHQpLGk9L14oPzpoZWFkfGxhc3QpJC8udGVzdCh0KSxvPWpyW2k/InRha2UiKygibGFzdCI9PXQ/IlJpZ2h0IjoiIik6dF0scz1pfHwvXmZpbmQvLnRlc3QodCk7byYmKGpyLnByb3RvdHlwZVt0XT1mdW5jdGlvbigpe3ZhciB0PXRoaXMuX193cmFwcGVkX18sYT1pP1sxXTphcmd1bWVudHMsYz10IGluc3RhbmNlb2YgcXIsbD1hWzBdLHU9Y3x8S3ModCksaD1mdW5jdGlvbihlKXt2YXIgdD1vLmFwcGx5KGpyLHh0KFtlXSxhKSk7cmV0dXJuIGkmJmY/dFswXTp0fTt1JiZyJiYiZnVuY3Rpb24iPT10eXBlb2YgbCYmMSE9bC5sZW5ndGgmJihjPXU9ITEpO3ZhciBmPXRoaXMuX19jaGFpbl9fLF89ISF0aGlzLl9fYWN0aW9uc19fLmxlbmd0aCxkPXMmJiFmLHA9YyYmIV87aWYoIXMmJnUpe3Q9cD90Om5ldyBxcih0aGlzKTt2YXIgdj1lLmFwcGx5KHQsYSk7cmV0dXJuIHYuX19hY3Rpb25zX18ucHVzaCh7ZnVuYzpkcyxhcmdzOltoXSx0aGlzQXJnOm59KSxuZXcgVXIodixmKX1yZXR1cm4gZCYmcD9lLmFwcGx5KHRoaXMsYSk6KHY9dGhpcy50aHJ1KGgpLGQ/aT92LnZhbHVlKClbMF06di52YWx1ZSgpOnYpfSl9KSksbXQoWyJwb3AiLCJwdXNoIiwic2hpZnQiLCJzb3J0Iiwic3BsaWNlIiwidW5zaGlmdCJdLChmdW5jdGlvbihlKXt2YXIgdD1rZVtlXSxyPS9eKD86cHVzaHxzb3J0fHVuc2hpZnQpJC8udGVzdChlKT8idGFwIjoidGhydSIsaT0vXig/OnBvcHxzaGlmdCkkLy50ZXN0KGUpO2pyLnByb3RvdHlwZVtlXT1mdW5jdGlvbigpe3ZhciBlPWFyZ3VtZW50cztpZihpJiYhdGhpcy5fX2NoYWluX18pe3ZhciBuPXRoaXMudmFsdWUoKTtyZXR1cm4gdC5hcHBseShLcyhuKT9uOltdLGUpfXJldHVybiB0aGlzW3JdKChmdW5jdGlvbihyKXtyZXR1cm4gdC5hcHBseShLcyhyKT9yOltdLGUpfSkpfX0pKSx5aShxci5wcm90b3R5cGUsKGZ1bmN0aW9uKGUsdCl7dmFyIHI9anJbdF07aWYocil7dmFyIGk9ci5uYW1lKyIiO0JlLmNhbGwoTXIsaSl8fChNcltpXT1bXSksTXJbaV0ucHVzaCh7bmFtZTp0LGZ1bmM6cn0pfX0pKSxNcltqbihuLDIpLm5hbWVdPVt7bmFtZToid3JhcHBlciIsZnVuYzpufV0scXIucHJvdG90eXBlLmNsb25lPWZ1bmN0aW9uKCl7dmFyIGU9bmV3IHFyKHRoaXMuX193cmFwcGVkX18pO3JldHVybiBlLl9fYWN0aW9uc19fPUFuKHRoaXMuX19hY3Rpb25zX18pLGUuX19kaXJfXz10aGlzLl9fZGlyX18sZS5fX2ZpbHRlcmVkX189dGhpcy5fX2ZpbHRlcmVkX18sZS5fX2l0ZXJhdGVlc19fPUFuKHRoaXMuX19pdGVyYXRlZXNfXyksZS5fX3Rha2VDb3VudF9fPXRoaXMuX190YWtlQ291bnRfXyxlLl9fdmlld3NfXz1Bbih0aGlzLl9fdmlld3NfXyksZX0scXIucHJvdG90eXBlLnJldmVyc2U9ZnVuY3Rpb24oKXtpZih0aGlzLl9fZmlsdGVyZWRfXyl7dmFyIGU9bmV3IHFyKHRoaXMpO2UuX19kaXJfXz0tMSxlLl9fZmlsdGVyZWRfXz0hMH1lbHNlKGU9dGhpcy5jbG9uZSgpKS5fX2Rpcl9fKj0tMTtyZXR1cm4gZX0scXIucHJvdG90eXBlLnZhbHVlPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5fX3dyYXBwZWRfXy52YWx1ZSgpLHQ9dGhpcy5fX2Rpcl9fLHI9S3MoZSksaT10PDAsbj1yP2UubGVuZ3RoOjAsbz1mdW5jdGlvbihlLHQscil7Zm9yKHZhciBpPS0xLG49ci5sZW5ndGg7KytpPG47KXt2YXIgbz1yW2ldLHM9by5zaXplO3N3aXRjaChvLnR5cGUpe2Nhc2UiZHJvcCI6ZSs9czticmVhaztjYXNlImRyb3BSaWdodCI6dC09czticmVhaztjYXNlInRha2UiOnQ9Z3IodCxlK3MpO2JyZWFrO2Nhc2UidGFrZVJpZ2h0IjplPXZyKGUsdC1zKX19cmV0dXJue3N0YXJ0OmUsZW5kOnR9fSgwLG4sdGhpcy5fX3ZpZXdzX18pLHM9by5zdGFydCxhPW8uZW5kLGM9YS1zLGw9aT9hOnMtMSx1PXRoaXMuX19pdGVyYXRlZXNfXyxoPXUubGVuZ3RoLGY9MCxfPWdyKGMsdGhpcy5fX3Rha2VDb3VudF9fKTtpZighcnx8IWkmJm49PWMmJl89PWMpcmV0dXJuIGZuKGUsdGhpcy5fX2FjdGlvbnNfXyk7dmFyIGQ9W107ZTpmb3IoO2MtLSYmZjxfOyl7Zm9yKHZhciBwPS0xLHY9ZVtsKz10XTsrK3A8aDspe3ZhciBnPXVbcF0seT1nLml0ZXJhdGVlLG09Zy50eXBlLGI9eSh2KTtpZigyPT1tKXY9YjtlbHNlIGlmKCFiKXtpZigxPT1tKWNvbnRpbnVlIGU7YnJlYWsgZX19ZFtmKytdPXZ9cmV0dXJuIGR9LGpyLnByb3RvdHlwZS5hdD1wcyxqci5wcm90b3R5cGUuY2hhaW49ZnVuY3Rpb24oKXtyZXR1cm4gX3ModGhpcyl9LGpyLnByb3RvdHlwZS5jb21taXQ9ZnVuY3Rpb24oKXtyZXR1cm4gbmV3IFVyKHRoaXMudmFsdWUoKSx0aGlzLl9fY2hhaW5fXyl9LGpyLnByb3RvdHlwZS5uZXh0PWZ1bmN0aW9uKCl7dGhpcy5fX3ZhbHVlc19fPT09biYmKHRoaXMuX192YWx1ZXNfXz1fYSh0aGlzLnZhbHVlKCkpKTt2YXIgZT10aGlzLl9faW5kZXhfXz49dGhpcy5fX3ZhbHVlc19fLmxlbmd0aDtyZXR1cm57ZG9uZTplLHZhbHVlOmU/bjp0aGlzLl9fdmFsdWVzX19bdGhpcy5fX2luZGV4X18rK119fSxqci5wcm90b3R5cGUucGxhbnQ9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0LHI9dGhpcztyIGluc3RhbmNlb2YgV3I7KXt2YXIgaT1XbyhyKTtpLl9faW5kZXhfXz0wLGkuX192YWx1ZXNfXz1uLHQ/by5fX3dyYXBwZWRfXz1pOnQ9aTt2YXIgbz1pO3I9ci5fX3dyYXBwZWRfX31yZXR1cm4gby5fX3dyYXBwZWRfXz1lLHR9LGpyLnByb3RvdHlwZS5yZXZlcnNlPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5fX3dyYXBwZWRfXztpZihlIGluc3RhbmNlb2YgcXIpe3ZhciB0PWU7cmV0dXJuIHRoaXMuX19hY3Rpb25zX18ubGVuZ3RoJiYodD1uZXcgcXIodGhpcykpLCh0PXQucmV2ZXJzZSgpKS5fX2FjdGlvbnNfXy5wdXNoKHtmdW5jOmRzLGFyZ3M6W3RzXSx0aGlzQXJnOm59KSxuZXcgVXIodCx0aGlzLl9fY2hhaW5fXyl9cmV0dXJuIHRoaXMudGhydSh0cyl9LGpyLnByb3RvdHlwZS50b0pTT049anIucHJvdG90eXBlLnZhbHVlT2Y9anIucHJvdG90eXBlLnZhbHVlPWZ1bmN0aW9uKCl7cmV0dXJuIGZuKHRoaXMuX193cmFwcGVkX18sdGhpcy5fX2FjdGlvbnNfXyl9LGpyLnByb3RvdHlwZS5maXJzdD1qci5wcm90b3R5cGUuaGVhZCxzdCYmKGpyLnByb3RvdHlwZVtzdF09ZnVuY3Rpb24oKXtyZXR1cm4gdGhpc30pLGpyfSgpO290Ll89Y3IsKGk9ZnVuY3Rpb24oKXtyZXR1cm4gY3J9LmNhbGwodCxyLHQsZSkpPT09bnx8KGUuZXhwb3J0cz1pKX0uY2FsbCh0aGlzKX0sMzc5OmU9PnsidXNlIHN0cmljdCI7dmFyIHQ9W107ZnVuY3Rpb24gcihlKXtmb3IodmFyIHI9LTEsaT0wO2k8dC5sZW5ndGg7aSsrKWlmKHRbaV0uaWRlbnRpZmllcj09PWUpe3I9aTticmVha31yZXR1cm4gcn1mdW5jdGlvbiBpKGUsaSl7Zm9yKHZhciBvPXt9LHM9W10sYT0wO2E8ZS5sZW5ndGg7YSsrKXt2YXIgYz1lW2FdLGw9aS5iYXNlP2NbMF0raS5iYXNlOmNbMF0sdT1vW2xdfHwwLGg9IiIuY29uY2F0KGwsIiAiKS5jb25jYXQodSk7b1tsXT11KzE7dmFyIGY9cihoKSxfPXtjc3M6Y1sxXSxtZWRpYTpjWzJdLHNvdXJjZU1hcDpjWzNdLHN1cHBvcnRzOmNbNF0sbGF5ZXI6Y1s1XX07aWYoLTEhPT1mKXRbZl0ucmVmZXJlbmNlcysrLHRbZl0udXBkYXRlcihfKTtlbHNle3ZhciBkPW4oXyxpKTtpLmJ5SW5kZXg9YSx0LnNwbGljZShhLDAse2lkZW50aWZpZXI6aCx1cGRhdGVyOmQscmVmZXJlbmNlczoxfSl9cy5wdXNoKGgpfXJldHVybiBzfWZ1bmN0aW9uIG4oZSx0KXt2YXIgcj10LmRvbUFQSSh0KTtyZXR1cm4gci51cGRhdGUoZSksZnVuY3Rpb24odCl7aWYodCl7aWYodC5jc3M9PT1lLmNzcyYmdC5tZWRpYT09PWUubWVkaWEmJnQuc291cmNlTWFwPT09ZS5zb3VyY2VNYXAmJnQuc3VwcG9ydHM9PT1lLnN1cHBvcnRzJiZ0LmxheWVyPT09ZS5sYXllcilyZXR1cm47ci51cGRhdGUoZT10KX1lbHNlIHIucmVtb3ZlKCl9fWUuZXhwb3J0cz1mdW5jdGlvbihlLG4pe3ZhciBvPWkoZT1lfHxbXSxuPW58fHt9KTtyZXR1cm4gZnVuY3Rpb24oZSl7ZT1lfHxbXTtmb3IodmFyIHM9MDtzPG8ubGVuZ3RoO3MrKyl7dmFyIGE9cihvW3NdKTt0W2FdLnJlZmVyZW5jZXMtLX1mb3IodmFyIGM9aShlLG4pLGw9MDtsPG8ubGVuZ3RoO2wrKyl7dmFyIHU9cihvW2xdKTswPT09dFt1XS5yZWZlcmVuY2VzJiYodFt1XS51cGRhdGVyKCksdC5zcGxpY2UodSwxKSl9bz1jfX19LDU2OTplPT57InVzZSBzdHJpY3QiO3ZhciB0PXt9O2UuZXhwb3J0cz1mdW5jdGlvbihlLHIpe3ZhciBpPWZ1bmN0aW9uKGUpe2lmKHZvaWQgMD09PXRbZV0pe3ZhciByPWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoZSk7aWYod2luZG93LkhUTUxJRnJhbWVFbGVtZW50JiZyIGluc3RhbmNlb2Ygd2luZG93LkhUTUxJRnJhbWVFbGVtZW50KXRyeXtyPXIuY29udGVudERvY3VtZW50LmhlYWR9Y2F0Y2goZSl7cj1udWxsfXRbZV09cn1yZXR1cm4gdFtlXX0oZSk7aWYoIWkpdGhyb3cgbmV3IEVycm9yKCJDb3VsZG4ndCBmaW5kIGEgc3R5bGUgdGFyZ2V0LiBUaGlzIHByb2JhYmx5IG1lYW5zIHRoYXQgdGhlIHZhbHVlIGZvciB0aGUgJ2luc2VydCcgcGFyYW1ldGVyIGlzIGludmFsaWQuIik7aS5hcHBlbmRDaGlsZChyKX19LDIxNjplPT57InVzZSBzdHJpY3QiO2UuZXhwb3J0cz1mdW5jdGlvbihlKXt2YXIgdD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzdHlsZSIpO3JldHVybiBlLnNldEF0dHJpYnV0ZXModCxlLmF0dHJpYnV0ZXMpLGUuaW5zZXJ0KHQsZS5vcHRpb25zKSx0fX0sNTY1OihlLHQscik9PnsidXNlIHN0cmljdCI7ZS5leHBvcnRzPWZ1bmN0aW9uKGUpe3ZhciB0PXIubmM7dCYmZS5zZXRBdHRyaWJ1dGUoIm5vbmNlIix0KX19LDc5NTplPT57InVzZSBzdHJpY3QiO2UuZXhwb3J0cz1mdW5jdGlvbihlKXt2YXIgdD1lLmluc2VydFN0eWxlRWxlbWVudChlKTtyZXR1cm57dXBkYXRlOmZ1bmN0aW9uKHIpeyFmdW5jdGlvbihlLHQscil7dmFyIGk9IiI7ci5zdXBwb3J0cyYmKGkrPSJAc3VwcG9ydHMgKCIuY29uY2F0KHIuc3VwcG9ydHMsIikgeyIpKSxyLm1lZGlhJiYoaSs9IkBtZWRpYSAiLmNvbmNhdChyLm1lZGlhLCIgeyIpKTt2YXIgbj12b2lkIDAhPT1yLmxheWVyO24mJihpKz0iQGxheWVyIi5jb25jYXQoci5sYXllci5sZW5ndGg+MD8iICIuY29uY2F0KHIubGF5ZXIpOiIiLCIgeyIpKSxpKz1yLmNzcyxuJiYoaSs9In0iKSxyLm1lZGlhJiYoaSs9In0iKSxyLnN1cHBvcnRzJiYoaSs9In0iKTt2YXIgbz1yLnNvdXJjZU1hcDtvJiYidW5kZWZpbmVkIiE9dHlwZW9mIGJ0b2EmJihpKz0iXG4vKiMgc291cmNlTWFwcGluZ1VSTD1kYXRhOmFwcGxpY2F0aW9uL2pzb247YmFzZTY0LCIuY29uY2F0KGJ0b2EodW5lc2NhcGUoZW5jb2RlVVJJQ29tcG9uZW50KEpTT04uc3RyaW5naWZ5KG8pKSkpLCIgKi8iKSksdC5zdHlsZVRhZ1RyYW5zZm9ybShpLGUsdC5vcHRpb25zKX0odCxlLHIpfSxyZW1vdmU6ZnVuY3Rpb24oKXshZnVuY3Rpb24oZSl7aWYobnVsbD09PWUucGFyZW50Tm9kZSlyZXR1cm4hMTtlLnBhcmVudE5vZGUucmVtb3ZlQ2hpbGQoZSl9KHQpfX19fSw1ODk6ZT0+eyJ1c2Ugc3RyaWN0IjtlLmV4cG9ydHM9ZnVuY3Rpb24oZSx0KXtpZih0LnN0eWxlU2hlZXQpdC5zdHlsZVNoZWV0LmNzc1RleHQ9ZTtlbHNle2Zvcig7dC5maXJzdENoaWxkOyl0LnJlbW92ZUNoaWxkKHQuZmlyc3RDaGlsZCk7dC5hcHBlbmRDaGlsZChkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShlKSl9fX0sNjE3OmU9PntzZWxmLGUuZXhwb3J0cz0oKCk9PnsidXNlIHN0cmljdCI7dmFyIGU9ezc3NTooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkZpdEFkZG9uPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt9cmV0dXJuIGUucHJvdG90eXBlLmFjdGl2YXRlPWZ1bmN0aW9uKGUpe3RoaXMuX3Rlcm1pbmFsPWV9LGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt9LGUucHJvdG90eXBlLmZpdD1mdW5jdGlvbigpe3ZhciBlPXRoaXMucHJvcG9zZURpbWVuc2lvbnMoKTtpZihlJiZ0aGlzLl90ZXJtaW5hbCl7dmFyIHQ9dGhpcy5fdGVybWluYWwuX2NvcmU7dGhpcy5fdGVybWluYWwucm93cz09PWUucm93cyYmdGhpcy5fdGVybWluYWwuY29scz09PWUuY29sc3x8KHQuX3JlbmRlclNlcnZpY2UuY2xlYXIoKSx0aGlzLl90ZXJtaW5hbC5yZXNpemUoZS5jb2xzLGUucm93cykpfX0sZS5wcm90b3R5cGUucHJvcG9zZURpbWVuc2lvbnM9ZnVuY3Rpb24oKXtpZih0aGlzLl90ZXJtaW5hbCYmdGhpcy5fdGVybWluYWwuZWxlbWVudCYmdGhpcy5fdGVybWluYWwuZWxlbWVudC5wYXJlbnRFbGVtZW50KXt2YXIgZT10aGlzLl90ZXJtaW5hbC5fY29yZTtpZigwIT09ZS5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLmFjdHVhbENlbGxXaWR0aCYmMCE9PWUuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KXt2YXIgdD13aW5kb3cuZ2V0Q29tcHV0ZWRTdHlsZSh0aGlzLl90ZXJtaW5hbC5lbGVtZW50LnBhcmVudEVsZW1lbnQpLHI9cGFyc2VJbnQodC5nZXRQcm9wZXJ0eVZhbHVlKCJoZWlnaHQiKSksaT1NYXRoLm1heCgwLHBhcnNlSW50KHQuZ2V0UHJvcGVydHlWYWx1ZSgid2lkdGgiKSkpLG49d2luZG93LmdldENvbXB1dGVkU3R5bGUodGhpcy5fdGVybWluYWwuZWxlbWVudCksbz1yLShwYXJzZUludChuLmdldFByb3BlcnR5VmFsdWUoInBhZGRpbmctdG9wIikpK3BhcnNlSW50KG4uZ2V0UHJvcGVydHlWYWx1ZSgicGFkZGluZy1ib3R0b20iKSkpLHM9aS0ocGFyc2VJbnQobi5nZXRQcm9wZXJ0eVZhbHVlKCJwYWRkaW5nLXJpZ2h0IikpK3BhcnNlSW50KG4uZ2V0UHJvcGVydHlWYWx1ZSgicGFkZGluZy1sZWZ0IikpKS1lLnZpZXdwb3J0LnNjcm9sbEJhcldpZHRoO3JldHVybntjb2xzOk1hdGgubWF4KDIsTWF0aC5mbG9vcihzL2UuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsV2lkdGgpKSxyb3dzOk1hdGgubWF4KDEsTWF0aC5mbG9vcihvL2UuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KSl9fX19LGV9KCk7dC5GaXRBZGRvbj1yfX0sdD17fTtyZXR1cm4gZnVuY3Rpb24gcihpKXtpZih0W2ldKXJldHVybiB0W2ldLmV4cG9ydHM7dmFyIG49dFtpXT17ZXhwb3J0czp7fX07cmV0dXJuIGVbaV0obixuLmV4cG9ydHMsciksbi5leHBvcnRzfSg3NzUpfSkoKX0sMzIwOmU9PntzZWxmLGUuZXhwb3J0cz0oKCk9PnsidXNlIHN0cmljdCI7dmFyIGU9ezQ1Njc6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQWNjZXNzaWJpbGl0eU1hbmFnZXI9dm9pZCAwO3ZhciBvPXIoOTA0Mikscz1yKDYxMTQpLGE9cig5OTI0KSxjPXIoMzY1NiksbD1yKDg0NCksdT1yKDU1OTYpLGg9cig5NjMxKSxmPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyKXt2YXIgaT1lLmNhbGwodGhpcyl8fHRoaXM7aS5fdGVybWluYWw9dCxpLl9yZW5kZXJTZXJ2aWNlPXIsaS5fbGl2ZVJlZ2lvbkxpbmVDb3VudD0wLGkuX2NoYXJzVG9Db25zdW1lPVtdLGkuX2NoYXJzVG9Bbm5vdW5jZT0iIixpLl9hY2Nlc3NpYmlsaXR5VHJlZVJvb3Q9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2IiksaS5fYWNjZXNzaWJpbGl0eVRyZWVSb290LnNldEF0dHJpYnV0ZSgicm9sZSIsImRvY3VtZW50IiksaS5fYWNjZXNzaWJpbGl0eVRyZWVSb290LmNsYXNzTGlzdC5hZGQoInh0ZXJtLWFjY2Vzc2liaWxpdHkiKSxpLl9hY2Nlc3NpYmlsaXR5VHJlZVJvb3QudGFiSW5kZXg9MCxpLl9yb3dDb250YWluZXI9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2IiksaS5fcm93Q29udGFpbmVyLnNldEF0dHJpYnV0ZSgicm9sZSIsImxpc3QiKSxpLl9yb3dDb250YWluZXIuY2xhc3NMaXN0LmFkZCgieHRlcm0tYWNjZXNzaWJpbGl0eS10cmVlIiksaS5fcm93RWxlbWVudHM9W107Zm9yKHZhciBuPTA7bjxpLl90ZXJtaW5hbC5yb3dzO24rKylpLl9yb3dFbGVtZW50c1tuXT1pLl9jcmVhdGVBY2Nlc3NpYmlsaXR5VHJlZU5vZGUoKSxpLl9yb3dDb250YWluZXIuYXBwZW5kQ2hpbGQoaS5fcm93RWxlbWVudHNbbl0pO2lmKGkuX3RvcEJvdW5kYXJ5Rm9jdXNMaXN0ZW5lcj1mdW5jdGlvbihlKXtyZXR1cm4gaS5fb25Cb3VuZGFyeUZvY3VzKGUsMCl9LGkuX2JvdHRvbUJvdW5kYXJ5Rm9jdXNMaXN0ZW5lcj1mdW5jdGlvbihlKXtyZXR1cm4gaS5fb25Cb3VuZGFyeUZvY3VzKGUsMSl9LGkuX3Jvd0VsZW1lbnRzWzBdLmFkZEV2ZW50TGlzdGVuZXIoImZvY3VzIixpLl90b3BCb3VuZGFyeUZvY3VzTGlzdGVuZXIpLGkuX3Jvd0VsZW1lbnRzW2kuX3Jvd0VsZW1lbnRzLmxlbmd0aC0xXS5hZGRFdmVudExpc3RlbmVyKCJmb2N1cyIsaS5fYm90dG9tQm91bmRhcnlGb2N1c0xpc3RlbmVyKSxpLl9yZWZyZXNoUm93c0RpbWVuc2lvbnMoKSxpLl9hY2Nlc3NpYmlsaXR5VHJlZVJvb3QuYXBwZW5kQ2hpbGQoaS5fcm93Q29udGFpbmVyKSxpLl9yZW5kZXJSb3dzRGVib3VuY2VyPW5ldyBhLlRpbWVCYXNlZERlYm91bmNlcihpLl9yZW5kZXJSb3dzLmJpbmQoaSkpLGkuX3JlZnJlc2hSb3dzKCksaS5fbGl2ZVJlZ2lvbj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKSxpLl9saXZlUmVnaW9uLmNsYXNzTGlzdC5hZGQoImxpdmUtcmVnaW9uIiksaS5fbGl2ZVJlZ2lvbi5zZXRBdHRyaWJ1dGUoImFyaWEtbGl2ZSIsImFzc2VydGl2ZSIpLGkuX2FjY2Vzc2liaWxpdHlUcmVlUm9vdC5hcHBlbmRDaGlsZChpLl9saXZlUmVnaW9uKSwhaS5fdGVybWluYWwuZWxlbWVudCl0aHJvdyBuZXcgRXJyb3IoIkNhbm5vdCBlbmFibGUgYWNjZXNzaWJpbGl0eSBiZWZvcmUgVGVybWluYWwub3BlbiIpO3JldHVybiBpLl90ZXJtaW5hbC5lbGVtZW50Lmluc2VydEFkamFjZW50RWxlbWVudCgiYWZ0ZXJiZWdpbiIsaS5fYWNjZXNzaWJpbGl0eVRyZWVSb290KSxpLnJlZ2lzdGVyKGkuX3JlbmRlclJvd3NEZWJvdW5jZXIpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25SZXNpemUoKGZ1bmN0aW9uKGUpe3JldHVybiBpLl9vblJlc2l6ZShlLnJvd3MpfSkpKSxpLnJlZ2lzdGVyKGkuX3Rlcm1pbmFsLm9uUmVuZGVyKChmdW5jdGlvbihlKXtyZXR1cm4gaS5fcmVmcmVzaFJvd3MoZS5zdGFydCxlLmVuZCl9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25TY3JvbGwoKGZ1bmN0aW9uKCl7cmV0dXJuIGkuX3JlZnJlc2hSb3dzKCl9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25BMTF5Q2hhcigoZnVuY3Rpb24oZSl7cmV0dXJuIGkuX29uQ2hhcihlKX0pKSksaS5yZWdpc3RlcihpLl90ZXJtaW5hbC5vbkxpbmVGZWVkKChmdW5jdGlvbigpe3JldHVybiBpLl9vbkNoYXIoIlxuIil9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25BMTF5VGFiKChmdW5jdGlvbihlKXtyZXR1cm4gaS5fb25UYWIoZSl9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25LZXkoKGZ1bmN0aW9uKGUpe3JldHVybiBpLl9vbktleShlLmtleSl9KSkpLGkucmVnaXN0ZXIoaS5fdGVybWluYWwub25CbHVyKChmdW5jdGlvbigpe3JldHVybiBpLl9jbGVhckxpdmVSZWdpb24oKX0pKSksaS5yZWdpc3RlcihpLl9yZW5kZXJTZXJ2aWNlLm9uRGltZW5zaW9uc0NoYW5nZSgoZnVuY3Rpb24oKXtyZXR1cm4gaS5fcmVmcmVzaFJvd3NEaW1lbnNpb25zKCl9KSkpLGkuX3NjcmVlbkRwck1vbml0b3I9bmV3IHUuU2NyZWVuRHByTW9uaXRvcixpLnJlZ2lzdGVyKGkuX3NjcmVlbkRwck1vbml0b3IpLGkuX3NjcmVlbkRwck1vbml0b3Iuc2V0TGlzdGVuZXIoKGZ1bmN0aW9uKCl7cmV0dXJuIGkuX3JlZnJlc2hSb3dzRGltZW5zaW9ucygpfSkpLGkucmVnaXN0ZXIoKDAsYy5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHdpbmRvdywicmVzaXplIiwoZnVuY3Rpb24oKXtyZXR1cm4gaS5fcmVmcmVzaFJvd3NEaW1lbnNpb25zKCl9KSkpLGl9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7ZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpLCgwLGgucmVtb3ZlRWxlbWVudEZyb21QYXJlbnQpKHRoaXMuX2FjY2Vzc2liaWxpdHlUcmVlUm9vdCksdGhpcy5fcm93RWxlbWVudHMubGVuZ3RoPTB9LHQucHJvdG90eXBlLl9vbkJvdW5kYXJ5Rm9jdXM9ZnVuY3Rpb24oZSx0KXt2YXIgcj1lLnRhcmdldCxpPXRoaXMuX3Jvd0VsZW1lbnRzWzA9PT10PzE6dGhpcy5fcm93RWxlbWVudHMubGVuZ3RoLTJdO2lmKHIuZ2V0QXR0cmlidXRlKCJhcmlhLXBvc2luc2V0IikhPT0oMD09PXQ/IjEiOiIiK3RoaXMuX3Rlcm1pbmFsLmJ1ZmZlci5saW5lcy5sZW5ndGgpJiZlLnJlbGF0ZWRUYXJnZXQ9PT1pKXt2YXIgbixvO2lmKDA9PT10PyhuPXIsbz10aGlzLl9yb3dFbGVtZW50cy5wb3AoKSx0aGlzLl9yb3dDb250YWluZXIucmVtb3ZlQ2hpbGQobykpOihuPXRoaXMuX3Jvd0VsZW1lbnRzLnNoaWZ0KCksbz1yLHRoaXMuX3Jvd0NvbnRhaW5lci5yZW1vdmVDaGlsZChuKSksbi5yZW1vdmVFdmVudExpc3RlbmVyKCJmb2N1cyIsdGhpcy5fdG9wQm91bmRhcnlGb2N1c0xpc3RlbmVyKSxvLnJlbW92ZUV2ZW50TGlzdGVuZXIoImZvY3VzIix0aGlzLl9ib3R0b21Cb3VuZGFyeUZvY3VzTGlzdGVuZXIpLDA9PT10KXt2YXIgcz10aGlzLl9jcmVhdGVBY2Nlc3NpYmlsaXR5VHJlZU5vZGUoKTt0aGlzLl9yb3dFbGVtZW50cy51bnNoaWZ0KHMpLHRoaXMuX3Jvd0NvbnRhaW5lci5pbnNlcnRBZGphY2VudEVsZW1lbnQoImFmdGVyYmVnaW4iLHMpfWVsc2Ugcz10aGlzLl9jcmVhdGVBY2Nlc3NpYmlsaXR5VHJlZU5vZGUoKSx0aGlzLl9yb3dFbGVtZW50cy5wdXNoKHMpLHRoaXMuX3Jvd0NvbnRhaW5lci5hcHBlbmRDaGlsZChzKTt0aGlzLl9yb3dFbGVtZW50c1swXS5hZGRFdmVudExpc3RlbmVyKCJmb2N1cyIsdGhpcy5fdG9wQm91bmRhcnlGb2N1c0xpc3RlbmVyKSx0aGlzLl9yb3dFbGVtZW50c1t0aGlzLl9yb3dFbGVtZW50cy5sZW5ndGgtMV0uYWRkRXZlbnRMaXN0ZW5lcigiZm9jdXMiLHRoaXMuX2JvdHRvbUJvdW5kYXJ5Rm9jdXNMaXN0ZW5lciksdGhpcy5fdGVybWluYWwuc2Nyb2xsTGluZXMoMD09PXQ/LTE6MSksdGhpcy5fcm93RWxlbWVudHNbMD09PXQ/MTp0aGlzLl9yb3dFbGVtZW50cy5sZW5ndGgtMl0uZm9jdXMoKSxlLnByZXZlbnREZWZhdWx0KCksZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKX19LHQucHJvdG90eXBlLl9vblJlc2l6ZT1mdW5jdGlvbihlKXt0aGlzLl9yb3dFbGVtZW50c1t0aGlzLl9yb3dFbGVtZW50cy5sZW5ndGgtMV0ucmVtb3ZlRXZlbnRMaXN0ZW5lcigiZm9jdXMiLHRoaXMuX2JvdHRvbUJvdW5kYXJ5Rm9jdXNMaXN0ZW5lcik7Zm9yKHZhciB0PXRoaXMuX3Jvd0NvbnRhaW5lci5jaGlsZHJlbi5sZW5ndGg7dDx0aGlzLl90ZXJtaW5hbC5yb3dzO3QrKyl0aGlzLl9yb3dFbGVtZW50c1t0XT10aGlzLl9jcmVhdGVBY2Nlc3NpYmlsaXR5VHJlZU5vZGUoKSx0aGlzLl9yb3dDb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy5fcm93RWxlbWVudHNbdF0pO2Zvcig7dGhpcy5fcm93RWxlbWVudHMubGVuZ3RoPmU7KXRoaXMuX3Jvd0NvbnRhaW5lci5yZW1vdmVDaGlsZCh0aGlzLl9yb3dFbGVtZW50cy5wb3AoKSk7dGhpcy5fcm93RWxlbWVudHNbdGhpcy5fcm93RWxlbWVudHMubGVuZ3RoLTFdLmFkZEV2ZW50TGlzdGVuZXIoImZvY3VzIix0aGlzLl9ib3R0b21Cb3VuZGFyeUZvY3VzTGlzdGVuZXIpLHRoaXMuX3JlZnJlc2hSb3dzRGltZW5zaW9ucygpfSx0LnByb3RvdHlwZS5fY3JlYXRlQWNjZXNzaWJpbGl0eVRyZWVOb2RlPWZ1bmN0aW9uKCl7dmFyIGU9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iik7cmV0dXJuIGUuc2V0QXR0cmlidXRlKCJyb2xlIiwibGlzdGl0ZW0iKSxlLnRhYkluZGV4PS0xLHRoaXMuX3JlZnJlc2hSb3dEaW1lbnNpb25zKGUpLGV9LHQucHJvdG90eXBlLl9vblRhYj1mdW5jdGlvbihlKXtmb3IodmFyIHQ9MDt0PGU7dCsrKXRoaXMuX29uQ2hhcigiICIpfSx0LnByb3RvdHlwZS5fb25DaGFyPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7dGhpcy5fbGl2ZVJlZ2lvbkxpbmVDb3VudDwyMSYmKHRoaXMuX2NoYXJzVG9Db25zdW1lLmxlbmd0aD4wP3RoaXMuX2NoYXJzVG9Db25zdW1lLnNoaWZ0KCkhPT1lJiYodGhpcy5fY2hhcnNUb0Fubm91bmNlKz1lKTp0aGlzLl9jaGFyc1RvQW5ub3VuY2UrPWUsIlxuIj09PWUmJih0aGlzLl9saXZlUmVnaW9uTGluZUNvdW50KyssMjE9PT10aGlzLl9saXZlUmVnaW9uTGluZUNvdW50JiYodGhpcy5fbGl2ZVJlZ2lvbi50ZXh0Q29udGVudCs9by50b29NdWNoT3V0cHV0KSkscy5pc01hYyYmdGhpcy5fbGl2ZVJlZ2lvbi50ZXh0Q29udGVudCYmdGhpcy5fbGl2ZVJlZ2lvbi50ZXh0Q29udGVudC5sZW5ndGg+MCYmIXRoaXMuX2xpdmVSZWdpb24ucGFyZW50Tm9kZSYmc2V0VGltZW91dCgoZnVuY3Rpb24oKXt0Ll9hY2Nlc3NpYmlsaXR5VHJlZVJvb3QuYXBwZW5kQ2hpbGQodC5fbGl2ZVJlZ2lvbil9KSwwKSl9LHQucHJvdG90eXBlLl9jbGVhckxpdmVSZWdpb249ZnVuY3Rpb24oKXt0aGlzLl9saXZlUmVnaW9uLnRleHRDb250ZW50PSIiLHRoaXMuX2xpdmVSZWdpb25MaW5lQ291bnQ9MCxzLmlzTWFjJiYoMCxoLnJlbW92ZUVsZW1lbnRGcm9tUGFyZW50KSh0aGlzLl9saXZlUmVnaW9uKX0sdC5wcm90b3R5cGUuX29uS2V5PWZ1bmN0aW9uKGUpe3RoaXMuX2NsZWFyTGl2ZVJlZ2lvbigpLHRoaXMuX2NoYXJzVG9Db25zdW1lLnB1c2goZSl9LHQucHJvdG90eXBlLl9yZWZyZXNoUm93cz1mdW5jdGlvbihlLHQpe3RoaXMuX3JlbmRlclJvd3NEZWJvdW5jZXIucmVmcmVzaChlLHQsdGhpcy5fdGVybWluYWwucm93cyl9LHQucHJvdG90eXBlLl9yZW5kZXJSb3dzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRoaXMuX3Rlcm1pbmFsLmJ1ZmZlcixpPXIubGluZXMubGVuZ3RoLnRvU3RyaW5nKCksbj1lO248PXQ7bisrKXt2YXIgbz1yLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhyLnlkaXNwK24sITApLHM9KHIueWRpc3ArbisxKS50b1N0cmluZygpLGE9dGhpcy5fcm93RWxlbWVudHNbbl07YSYmKDA9PT1vLmxlbmd0aD9hLmlubmVyVGV4dD0iwqAiOmEudGV4dENvbnRlbnQ9byxhLnNldEF0dHJpYnV0ZSgiYXJpYS1wb3NpbnNldCIscyksYS5zZXRBdHRyaWJ1dGUoImFyaWEtc2V0c2l6ZSIsaSkpfXRoaXMuX2Fubm91bmNlQ2hhcmFjdGVycygpfSx0LnByb3RvdHlwZS5fcmVmcmVzaFJvd3NEaW1lbnNpb25zPWZ1bmN0aW9uKCl7aWYodGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLmFjdHVhbENlbGxIZWlnaHQpe3RoaXMuX3Jvd0VsZW1lbnRzLmxlbmd0aCE9PXRoaXMuX3Rlcm1pbmFsLnJvd3MmJnRoaXMuX29uUmVzaXplKHRoaXMuX3Rlcm1pbmFsLnJvd3MpO2Zvcih2YXIgZT0wO2U8dGhpcy5fdGVybWluYWwucm93cztlKyspdGhpcy5fcmVmcmVzaFJvd0RpbWVuc2lvbnModGhpcy5fcm93RWxlbWVudHNbZV0pfX0sdC5wcm90b3R5cGUuX3JlZnJlc2hSb3dEaW1lbnNpb25zPWZ1bmN0aW9uKGUpe2Uuc3R5bGUuaGVpZ2h0PXRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KyJweCJ9LHQucHJvdG90eXBlLl9hbm5vdW5jZUNoYXJhY3RlcnM9ZnVuY3Rpb24oKXswIT09dGhpcy5fY2hhcnNUb0Fubm91bmNlLmxlbmd0aCYmKHRoaXMuX2xpdmVSZWdpb24udGV4dENvbnRlbnQrPXRoaXMuX2NoYXJzVG9Bbm5vdW5jZSx0aGlzLl9jaGFyc1RvQW5ub3VuY2U9IiIpfSx0fShsLkRpc3Bvc2FibGUpO3QuQWNjZXNzaWJpbGl0eU1hbmFnZXI9Zn0sMzYxNDooZSx0KT0+e2Z1bmN0aW9uIHIoZSl7cmV0dXJuIGUucmVwbGFjZSgvXHI/XG4vZywiXHIiKX1mdW5jdGlvbiBpKGUsdCl7cmV0dXJuIHQ/IhtbMjAwfiIrZSsiG1syMDF+IjplfWZ1bmN0aW9uIG4oZSx0LG4pe2U9aShlPXIoZSksbi5kZWNQcml2YXRlTW9kZXMuYnJhY2tldGVkUGFzdGVNb2RlKSxuLnRyaWdnZXJEYXRhRXZlbnQoZSwhMCksdC52YWx1ZT0iIn1mdW5jdGlvbiBvKGUsdCxyKXt2YXIgaT1yLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLG49ZS5jbGllbnRYLWkubGVmdC0xMCxvPWUuY2xpZW50WS1pLnRvcC0xMDt0LnN0eWxlLndpZHRoPSIyMHB4Iix0LnN0eWxlLmhlaWdodD0iMjBweCIsdC5zdHlsZS5sZWZ0PW4rInB4Iix0LnN0eWxlLnRvcD1vKyJweCIsdC5zdHlsZS56SW5kZXg9IjEwMDAiLHQuZm9jdXMoKX1PYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5yaWdodENsaWNrSGFuZGxlcj10Lm1vdmVUZXh0QXJlYVVuZGVyTW91c2VDdXJzb3I9dC5wYXN0ZT10LmhhbmRsZVBhc3RlRXZlbnQ9dC5jb3B5SGFuZGxlcj10LmJyYWNrZXRUZXh0Rm9yUGFzdGU9dC5wcmVwYXJlVGV4dEZvclRlcm1pbmFsPXZvaWQgMCx0LnByZXBhcmVUZXh0Rm9yVGVybWluYWw9cix0LmJyYWNrZXRUZXh0Rm9yUGFzdGU9aSx0LmNvcHlIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7ZS5jbGlwYm9hcmREYXRhJiZlLmNsaXBib2FyZERhdGEuc2V0RGF0YSgidGV4dC9wbGFpbiIsdC5zZWxlY3Rpb25UZXh0KSxlLnByZXZlbnREZWZhdWx0KCl9LHQuaGFuZGxlUGFzdGVFdmVudD1mdW5jdGlvbihlLHQscil7ZS5zdG9wUHJvcGFnYXRpb24oKSxlLmNsaXBib2FyZERhdGEmJm4oZS5jbGlwYm9hcmREYXRhLmdldERhdGEoInRleHQvcGxhaW4iKSx0LHIpfSx0LnBhc3RlPW4sdC5tb3ZlVGV4dEFyZWFVbmRlck1vdXNlQ3Vyc29yPW8sdC5yaWdodENsaWNrSGFuZGxlcj1mdW5jdGlvbihlLHQscixpLG4pe28oZSx0LHIpLG4mJmkucmlnaHRDbGlja1NlbGVjdChlKSx0LnZhbHVlPWkuc2VsZWN0aW9uVGV4dCx0LnNlbGVjdCgpfX0sNDc3NDooZSx0KT0+e3ZhciByLGksbixvO2Z1bmN0aW9uIHMoZSl7dmFyIHQ9ZS50b1N0cmluZygxNik7cmV0dXJuIHQubGVuZ3RoPDI/IjAiK3Q6dH1mdW5jdGlvbiBhKGUsdCl7cmV0dXJuIGU8dD8odCsuMDUpLyhlKy4wNSk6KGUrLjA1KS8odCsuMDUpfU9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LmNvbnRyYXN0UmF0aW89dC50b1BhZGRlZEhleD10LnJnYmE9dC5yZ2I9dC5jc3M9dC5jb2xvcj10LmNoYW5uZWxzPXZvaWQgMCxmdW5jdGlvbihlKXtlLnRvQ3NzPWZ1bmN0aW9uKGUsdCxyLGkpe3JldHVybiB2b2lkIDAhPT1pPyIjIitzKGUpK3ModCkrcyhyKStzKGkpOiIjIitzKGUpK3ModCkrcyhyKX0sZS50b1JnYmE9ZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuIHZvaWQgMD09PWkmJihpPTI1NSksKGU8PDI0fHQ8PDE2fHI8PDh8aSk+Pj4wfX0ocj10LmNoYW5uZWxzfHwodC5jaGFubmVscz17fSkpLChpPXQuY29sb3J8fCh0LmNvbG9yPXt9KSkuYmxlbmQ9ZnVuY3Rpb24oZSx0KXt2YXIgaT0oMjU1JnQucmdiYSkvMjU1O2lmKDE9PT1pKXJldHVybntjc3M6dC5jc3MscmdiYTp0LnJnYmF9O3ZhciBuPXQucmdiYT4+MjQmMjU1LG89dC5yZ2JhPj4xNiYyNTUscz10LnJnYmE+PjgmMjU1LGE9ZS5yZ2JhPj4yNCYyNTUsYz1lLnJnYmE+PjE2JjI1NSxsPWUucmdiYT4+OCYyNTUsdT1hK01hdGgucm91bmQoKG4tYSkqaSksaD1jK01hdGgucm91bmQoKG8tYykqaSksZj1sK01hdGgucm91bmQoKHMtbCkqaSk7cmV0dXJue2NzczpyLnRvQ3NzKHUsaCxmKSxyZ2JhOnIudG9SZ2JhKHUsaCxmKX19LGkuaXNPcGFxdWU9ZnVuY3Rpb24oZSl7cmV0dXJuIDI1NT09KDI1NSZlLnJnYmEpfSxpLmVuc3VyZUNvbnRyYXN0UmF0aW89ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPW8uZW5zdXJlQ29udHJhc3RSYXRpbyhlLnJnYmEsdC5yZ2JhLHIpO2lmKGkpcmV0dXJuIG8udG9Db2xvcihpPj4yNCYyNTUsaT4+MTYmMjU1LGk+PjgmMjU1KX0saS5vcGFxdWU9ZnVuY3Rpb24oZSl7dmFyIHQ9KDI1NXxlLnJnYmEpPj4+MCxpPW8udG9DaGFubmVscyh0KSxuPWlbMF0scz1pWzFdLGE9aVsyXTtyZXR1cm57Y3NzOnIudG9Dc3MobixzLGEpLHJnYmE6dH19LGkub3BhY2l0eT1mdW5jdGlvbihlLHQpe3ZhciBpPU1hdGgucm91bmQoMjU1KnQpLG49by50b0NoYW5uZWxzKGUucmdiYSkscz1uWzBdLGE9blsxXSxjPW5bMl07cmV0dXJue2NzczpyLnRvQ3NzKHMsYSxjLGkpLHJnYmE6ci50b1JnYmEocyxhLGMsaSl9fSxpLnRvQ29sb3JSR0I9ZnVuY3Rpb24oZSl7cmV0dXJuW2UucmdiYT4+MjQmMjU1LGUucmdiYT4+MTYmMjU1LGUucmdiYT4+OCYyNTVdfSwodC5jc3N8fCh0LmNzcz17fSkpLnRvQ29sb3I9ZnVuY3Rpb24oZSl7c3dpdGNoKGUubGVuZ3RoKXtjYXNlIDc6cmV0dXJue2NzczplLHJnYmE6KHBhcnNlSW50KGUuc2xpY2UoMSksMTYpPDw4fDI1NSk+Pj4wfTtjYXNlIDk6cmV0dXJue2NzczplLHJnYmE6cGFyc2VJbnQoZS5zbGljZSgxKSwxNik+Pj4wfX10aHJvdyBuZXcgRXJyb3IoImNzcy50b0NvbG9yOiBVbnN1cHBvcnRlZCBjc3MgZm9ybWF0Iil9LGZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQoZSx0LHIpe3ZhciBpPWUvMjU1LG49dC8yNTUsbz1yLzI1NTtyZXR1cm4uMjEyNiooaTw9LjAzOTI4P2kvMTIuOTI6TWF0aC5wb3coKGkrLjA1NSkvMS4wNTUsMi40KSkrLjcxNTIqKG48PS4wMzkyOD9uLzEyLjkyOk1hdGgucG93KChuKy4wNTUpLzEuMDU1LDIuNCkpKy4wNzIyKihvPD0uMDM5Mjg/by8xMi45MjpNYXRoLnBvdygobysuMDU1KS8xLjA1NSwyLjQpKX1lLnJlbGF0aXZlTHVtaW5hbmNlPWZ1bmN0aW9uKGUpe3JldHVybiB0KGU+PjE2JjI1NSxlPj44JjI1NSwyNTUmZSl9LGUucmVsYXRpdmVMdW1pbmFuY2UyPXR9KG49dC5yZ2J8fCh0LnJnYj17fSkpLGZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQoZSx0LHIpe2Zvcih2YXIgaT1lPj4yNCYyNTUsbz1lPj4xNiYyNTUscz1lPj44JjI1NSxjPXQ+PjI0JjI1NSxsPXQ+PjE2JjI1NSx1PXQ+PjgmMjU1LGg9YShuLnJlbGF0aXZlTHVtaW5hbmNlMihjLHUsbCksbi5yZWxhdGl2ZUx1bWluYW5jZTIoaSxvLHMpKTtoPHImJihjPjB8fGw+MHx8dT4wKTspYy09TWF0aC5tYXgoMCxNYXRoLmNlaWwoLjEqYykpLGwtPU1hdGgubWF4KDAsTWF0aC5jZWlsKC4xKmwpKSx1LT1NYXRoLm1heCgwLE1hdGguY2VpbCguMSp1KSksaD1hKG4ucmVsYXRpdmVMdW1pbmFuY2UyKGMsdSxsKSxuLnJlbGF0aXZlTHVtaW5hbmNlMihpLG8scykpO3JldHVybihjPDwyNHxsPDwxNnx1PDw4fDI1NSk+Pj4wfWZ1bmN0aW9uIGkoZSx0LHIpe2Zvcih2YXIgaT1lPj4yNCYyNTUsbz1lPj4xNiYyNTUscz1lPj44JjI1NSxjPXQ+PjI0JjI1NSxsPXQ+PjE2JjI1NSx1PXQ+PjgmMjU1LGg9YShuLnJlbGF0aXZlTHVtaW5hbmNlMihjLHUsbCksbi5yZWxhdGl2ZUx1bWluYW5jZTIoaSxvLHMpKTtoPHImJihjPDI1NXx8bDwyNTV8fHU8MjU1KTspYz1NYXRoLm1pbigyNTUsYytNYXRoLmNlaWwoLjEqKDI1NS1jKSkpLGw9TWF0aC5taW4oMjU1LGwrTWF0aC5jZWlsKC4xKigyNTUtbCkpKSx1PU1hdGgubWluKDI1NSx1K01hdGguY2VpbCguMSooMjU1LXUpKSksaD1hKG4ucmVsYXRpdmVMdW1pbmFuY2UyKGMsdSxsKSxuLnJlbGF0aXZlTHVtaW5hbmNlMihpLG8scykpO3JldHVybihjPDwyNHxsPDwxNnx1PDw4fDI1NSk+Pj4wfWUuZW5zdXJlQ29udHJhc3RSYXRpbz1mdW5jdGlvbihlLHIsbyl7dmFyIHM9bi5yZWxhdGl2ZUx1bWluYW5jZShlPj44KSxjPW4ucmVsYXRpdmVMdW1pbmFuY2Uocj4+OCk7aWYoYShzLGMpPG8pcmV0dXJuIGM8cz90KGUscixvKTppKGUscixvKX0sZS5yZWR1Y2VMdW1pbmFuY2U9dCxlLmluY3JlYXNlTHVtaW5hbmNlPWksZS50b0NoYW5uZWxzPWZ1bmN0aW9uKGUpe3JldHVybltlPj4yNCYyNTUsZT4+MTYmMjU1LGU+PjgmMjU1LDI1NSZlXX0sZS50b0NvbG9yPWZ1bmN0aW9uKGUsdCxpKXtyZXR1cm57Y3NzOnIudG9Dc3MoZSx0LGkpLHJnYmE6ci50b1JnYmEoZSx0LGkpfX19KG89dC5yZ2JhfHwodC5yZ2JhPXt9KSksdC50b1BhZGRlZEhleD1zLHQuY29udHJhc3RSYXRpbz1hfSw3MjM5OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQ29sb3JDb250cmFzdENhY2hlPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt0aGlzLl9jb2xvcj17fSx0aGlzLl9yZ2JhPXt9fXJldHVybiBlLnByb3RvdHlwZS5jbGVhcj1mdW5jdGlvbigpe3RoaXMuX2NvbG9yPXt9LHRoaXMuX3JnYmE9e319LGUucHJvdG90eXBlLnNldENzcz1mdW5jdGlvbihlLHQscil7dGhpcy5fcmdiYVtlXXx8KHRoaXMuX3JnYmFbZV09e30pLHRoaXMuX3JnYmFbZV1bdF09cn0sZS5wcm90b3R5cGUuZ2V0Q3NzPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMuX3JnYmFbZV0/dGhpcy5fcmdiYVtlXVt0XTp2b2lkIDB9LGUucHJvdG90eXBlLnNldENvbG9yPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9jb2xvcltlXXx8KHRoaXMuX2NvbG9yW2VdPXt9KSx0aGlzLl9jb2xvcltlXVt0XT1yfSxlLnByb3RvdHlwZS5nZXRDb2xvcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9jb2xvcltlXT90aGlzLl9jb2xvcltlXVt0XTp2b2lkIDB9LGV9KCk7dC5Db2xvckNvbnRyYXN0Q2FjaGU9cn0sNTY4MDpmdW5jdGlvbihlLHQscil7dmFyIGk9dGhpcyYmdGhpcy5fX3NwcmVhZEFycmF5fHxmdW5jdGlvbihlLHQscil7aWYocnx8Mj09PWFyZ3VtZW50cy5sZW5ndGgpZm9yKHZhciBpLG49MCxvPXQubGVuZ3RoO248bztuKyspIWkmJm4gaW4gdHx8KGl8fChpPUFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHQsMCxuKSksaVtuXT10W25dKTtyZXR1cm4gZS5jb25jYXQoaXx8QXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwodCkpfTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Db2xvck1hbmFnZXI9dC5ERUZBVUxUX0FOU0lfQ09MT1JTPXZvaWQgMDt2YXIgbj1yKDQ3NzQpLG89cig3MjM5KSxzPW4uY3NzLnRvQ29sb3IoIiNmZmZmZmYiKSxhPW4uY3NzLnRvQ29sb3IoIiMwMDAwMDAiKSxjPW4uY3NzLnRvQ29sb3IoIiNmZmZmZmYiKSxsPW4uY3NzLnRvQ29sb3IoIiMwMDAwMDAiKSx1PXtjc3M6InJnYmEoMjU1LCAyNTUsIDI1NSwgMC4zKSIscmdiYTo0Mjk0OTY3MTE3fTt0LkRFRkFVTFRfQU5TSV9DT0xPUlM9T2JqZWN0LmZyZWV6ZShmdW5jdGlvbigpe2Zvcih2YXIgZT1bbi5jc3MudG9Db2xvcigiIzJlMzQzNiIpLG4uY3NzLnRvQ29sb3IoIiNjYzAwMDAiKSxuLmNzcy50b0NvbG9yKCIjNGU5YTA2Iiksbi5jc3MudG9Db2xvcigiI2M0YTAwMCIpLG4uY3NzLnRvQ29sb3IoIiMzNDY1YTQiKSxuLmNzcy50b0NvbG9yKCIjNzU1MDdiIiksbi5jc3MudG9Db2xvcigiIzA2OTg5YSIpLG4uY3NzLnRvQ29sb3IoIiNkM2Q3Y2YiKSxuLmNzcy50b0NvbG9yKCIjNTU1NzUzIiksbi5jc3MudG9Db2xvcigiI2VmMjkyOSIpLG4uY3NzLnRvQ29sb3IoIiM4YWUyMzQiKSxuLmNzcy50b0NvbG9yKCIjZmNlOTRmIiksbi5jc3MudG9Db2xvcigiIzcyOWZjZiIpLG4uY3NzLnRvQ29sb3IoIiNhZDdmYTgiKSxuLmNzcy50b0NvbG9yKCIjMzRlMmUyIiksbi5jc3MudG9Db2xvcigiI2VlZWVlYyIpXSx0PVswLDk1LDEzNSwxNzUsMjE1LDI1NV0scj0wO3I8MjE2O3IrKyl7dmFyIGk9dFtyLzM2JTZ8MF0sbz10W3IvNiU2fDBdLHM9dFtyJTZdO2UucHVzaCh7Y3NzOm4uY2hhbm5lbHMudG9Dc3MoaSxvLHMpLHJnYmE6bi5jaGFubmVscy50b1JnYmEoaSxvLHMpfSl9Zm9yKHI9MDtyPDI0O3IrKyl7dmFyIGE9OCsxMCpyO2UucHVzaCh7Y3NzOm4uY2hhbm5lbHMudG9Dc3MoYSxhLGEpLHJnYmE6bi5jaGFubmVscy50b1JnYmEoYSxhLGEpfSl9cmV0dXJuIGV9KCkpO3ZhciBoPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHIpe3RoaXMuYWxsb3dUcmFuc3BhcmVuY3k9cjt2YXIgaT1lLmNyZWF0ZUVsZW1lbnQoImNhbnZhcyIpO2kud2lkdGg9MSxpLmhlaWdodD0xO3ZhciBoPWkuZ2V0Q29udGV4dCgiMmQiKTtpZighaCl0aHJvdyBuZXcgRXJyb3IoIkNvdWxkIG5vdCBnZXQgcmVuZGVyaW5nIGNvbnRleHQiKTt0aGlzLl9jdHg9aCx0aGlzLl9jdHguZ2xvYmFsQ29tcG9zaXRlT3BlcmF0aW9uPSJjb3B5Iix0aGlzLl9saXRtdXNDb2xvcj10aGlzLl9jdHguY3JlYXRlTGluZWFyR3JhZGllbnQoMCwwLDEsMSksdGhpcy5fY29udHJhc3RDYWNoZT1uZXcgby5Db2xvckNvbnRyYXN0Q2FjaGUsdGhpcy5jb2xvcnM9e2ZvcmVncm91bmQ6cyxiYWNrZ3JvdW5kOmEsY3Vyc29yOmMsY3Vyc29yQWNjZW50Omwsc2VsZWN0aW9uVHJhbnNwYXJlbnQ6dSxzZWxlY3Rpb25PcGFxdWU6bi5jb2xvci5ibGVuZChhLHUpLGFuc2k6dC5ERUZBVUxUX0FOU0lfQ09MT1JTLnNsaWNlKCksY29udHJhc3RDYWNoZTp0aGlzLl9jb250cmFzdENhY2hlfSx0aGlzLl91cGRhdGVSZXN0b3JlQ29sb3JzKCl9cmV0dXJuIGUucHJvdG90eXBlLm9uT3B0aW9uc0NoYW5nZT1mdW5jdGlvbihlKXsibWluaW11bUNvbnRyYXN0UmF0aW8iPT09ZSYmdGhpcy5fY29udHJhc3RDYWNoZS5jbGVhcigpfSxlLnByb3RvdHlwZS5zZXRUaGVtZT1mdW5jdGlvbihlKXt2b2lkIDA9PT1lJiYoZT17fSksdGhpcy5jb2xvcnMuZm9yZWdyb3VuZD10aGlzLl9wYXJzZUNvbG9yKGUuZm9yZWdyb3VuZCxzKSx0aGlzLmNvbG9ycy5iYWNrZ3JvdW5kPXRoaXMuX3BhcnNlQ29sb3IoZS5iYWNrZ3JvdW5kLGEpLHRoaXMuY29sb3JzLmN1cnNvcj10aGlzLl9wYXJzZUNvbG9yKGUuY3Vyc29yLGMsITApLHRoaXMuY29sb3JzLmN1cnNvckFjY2VudD10aGlzLl9wYXJzZUNvbG9yKGUuY3Vyc29yQWNjZW50LGwsITApLHRoaXMuY29sb3JzLnNlbGVjdGlvblRyYW5zcGFyZW50PXRoaXMuX3BhcnNlQ29sb3IoZS5zZWxlY3Rpb24sdSwhMCksdGhpcy5jb2xvcnMuc2VsZWN0aW9uT3BhcXVlPW4uY29sb3IuYmxlbmQodGhpcy5jb2xvcnMuYmFja2dyb3VuZCx0aGlzLmNvbG9ycy5zZWxlY3Rpb25UcmFuc3BhcmVudCksbi5jb2xvci5pc09wYXF1ZSh0aGlzLmNvbG9ycy5zZWxlY3Rpb25UcmFuc3BhcmVudCkmJih0aGlzLmNvbG9ycy5zZWxlY3Rpb25UcmFuc3BhcmVudD1uLmNvbG9yLm9wYWNpdHkodGhpcy5jb2xvcnMuc2VsZWN0aW9uVHJhbnNwYXJlbnQsLjMpKSx0aGlzLmNvbG9ycy5hbnNpWzBdPXRoaXMuX3BhcnNlQ29sb3IoZS5ibGFjayx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbMF0pLHRoaXMuY29sb3JzLmFuc2lbMV09dGhpcy5fcGFyc2VDb2xvcihlLnJlZCx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbMV0pLHRoaXMuY29sb3JzLmFuc2lbMl09dGhpcy5fcGFyc2VDb2xvcihlLmdyZWVuLHQuREVGQVVMVF9BTlNJX0NPTE9SU1syXSksdGhpcy5jb2xvcnMuYW5zaVszXT10aGlzLl9wYXJzZUNvbG9yKGUueWVsbG93LHQuREVGQVVMVF9BTlNJX0NPTE9SU1szXSksdGhpcy5jb2xvcnMuYW5zaVs0XT10aGlzLl9wYXJzZUNvbG9yKGUuYmx1ZSx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbNF0pLHRoaXMuY29sb3JzLmFuc2lbNV09dGhpcy5fcGFyc2VDb2xvcihlLm1hZ2VudGEsdC5ERUZBVUxUX0FOU0lfQ09MT1JTWzVdKSx0aGlzLmNvbG9ycy5hbnNpWzZdPXRoaXMuX3BhcnNlQ29sb3IoZS5jeWFuLHQuREVGQVVMVF9BTlNJX0NPTE9SU1s2XSksdGhpcy5jb2xvcnMuYW5zaVs3XT10aGlzLl9wYXJzZUNvbG9yKGUud2hpdGUsdC5ERUZBVUxUX0FOU0lfQ09MT1JTWzddKSx0aGlzLmNvbG9ycy5hbnNpWzhdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRCbGFjayx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbOF0pLHRoaXMuY29sb3JzLmFuc2lbOV09dGhpcy5fcGFyc2VDb2xvcihlLmJyaWdodFJlZCx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbOV0pLHRoaXMuY29sb3JzLmFuc2lbMTBdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRHcmVlbix0LkRFRkFVTFRfQU5TSV9DT0xPUlNbMTBdKSx0aGlzLmNvbG9ycy5hbnNpWzExXT10aGlzLl9wYXJzZUNvbG9yKGUuYnJpZ2h0WWVsbG93LHQuREVGQVVMVF9BTlNJX0NPTE9SU1sxMV0pLHRoaXMuY29sb3JzLmFuc2lbMTJdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRCbHVlLHQuREVGQVVMVF9BTlNJX0NPTE9SU1sxMl0pLHRoaXMuY29sb3JzLmFuc2lbMTNdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRNYWdlbnRhLHQuREVGQVVMVF9BTlNJX0NPTE9SU1sxM10pLHRoaXMuY29sb3JzLmFuc2lbMTRdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRDeWFuLHQuREVGQVVMVF9BTlNJX0NPTE9SU1sxNF0pLHRoaXMuY29sb3JzLmFuc2lbMTVdPXRoaXMuX3BhcnNlQ29sb3IoZS5icmlnaHRXaGl0ZSx0LkRFRkFVTFRfQU5TSV9DT0xPUlNbMTVdKSx0aGlzLl9jb250cmFzdENhY2hlLmNsZWFyKCksdGhpcy5fdXBkYXRlUmVzdG9yZUNvbG9ycygpfSxlLnByb3RvdHlwZS5yZXN0b3JlQ29sb3I9ZnVuY3Rpb24oZSl7aWYodm9pZCAwIT09ZSlzd2l0Y2goZSl7Y2FzZSAyNTY6dGhpcy5jb2xvcnMuZm9yZWdyb3VuZD10aGlzLl9yZXN0b3JlQ29sb3JzLmZvcmVncm91bmQ7YnJlYWs7Y2FzZSAyNTc6dGhpcy5jb2xvcnMuYmFja2dyb3VuZD10aGlzLl9yZXN0b3JlQ29sb3JzLmJhY2tncm91bmQ7YnJlYWs7Y2FzZSAyNTg6dGhpcy5jb2xvcnMuY3Vyc29yPXRoaXMuX3Jlc3RvcmVDb2xvcnMuY3Vyc29yO2JyZWFrO2RlZmF1bHQ6dGhpcy5jb2xvcnMuYW5zaVtlXT10aGlzLl9yZXN0b3JlQ29sb3JzLmFuc2lbZV19ZWxzZSBmb3IodmFyIHQ9MDt0PHRoaXMuX3Jlc3RvcmVDb2xvcnMuYW5zaS5sZW5ndGg7Kyt0KXRoaXMuY29sb3JzLmFuc2lbdF09dGhpcy5fcmVzdG9yZUNvbG9ycy5hbnNpW3RdfSxlLnByb3RvdHlwZS5fdXBkYXRlUmVzdG9yZUNvbG9ycz1mdW5jdGlvbigpe3RoaXMuX3Jlc3RvcmVDb2xvcnM9e2ZvcmVncm91bmQ6dGhpcy5jb2xvcnMuZm9yZWdyb3VuZCxiYWNrZ3JvdW5kOnRoaXMuY29sb3JzLmJhY2tncm91bmQsY3Vyc29yOnRoaXMuY29sb3JzLmN1cnNvcixhbnNpOmkoW10sdGhpcy5jb2xvcnMuYW5zaSwhMCl9fSxlLnByb3RvdHlwZS5fcGFyc2VDb2xvcj1mdW5jdGlvbihlLHQscil7aWYodm9pZCAwPT09ciYmKHI9dGhpcy5hbGxvd1RyYW5zcGFyZW5jeSksdm9pZCAwPT09ZSlyZXR1cm4gdDtpZih0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2xpdG11c0NvbG9yLHRoaXMuX2N0eC5maWxsU3R5bGU9ZSwic3RyaW5nIiE9dHlwZW9mIHRoaXMuX2N0eC5maWxsU3R5bGUpcmV0dXJuIGNvbnNvbGUud2FybigiQ29sb3I6ICIrZSsiIGlzIGludmFsaWQgdXNpbmcgZmFsbGJhY2sgIit0LmNzcyksdDt0aGlzLl9jdHguZmlsbFJlY3QoMCwwLDEsMSk7dmFyIGk9dGhpcy5fY3R4LmdldEltYWdlRGF0YSgwLDAsMSwxKS5kYXRhO2lmKDI1NSE9PWlbM10pe2lmKCFyKXJldHVybiBjb25zb2xlLndhcm4oIkNvbG9yOiAiK2UrIiBpcyB1c2luZyB0cmFuc3BhcmVuY3ksIGJ1dCBhbGxvd1RyYW5zcGFyZW5jeSBpcyBmYWxzZS4gVXNpbmcgZmFsbGJhY2sgIit0LmNzcysiLiIpLHQ7dmFyIG89dGhpcy5fY3R4LmZpbGxTdHlsZS5zdWJzdHJpbmcoNSx0aGlzLl9jdHguZmlsbFN0eWxlLmxlbmd0aC0xKS5zcGxpdCgiLCIpLm1hcCgoZnVuY3Rpb24oZSl7cmV0dXJuIE51bWJlcihlKX0pKSxzPW9bMF0sYT1vWzFdLGM9b1syXSxsPW9bM10sdT1NYXRoLnJvdW5kKDI1NSpsKTtyZXR1cm57cmdiYTpuLmNoYW5uZWxzLnRvUmdiYShzLGEsYyx1KSxjc3M6ZX19cmV0dXJue2Nzczp0aGlzLl9jdHguZmlsbFN0eWxlLHJnYmE6bi5jaGFubmVscy50b1JnYmEoaVswXSxpWzFdLGlbMl0saVszXSl9fSxlfSgpO3QuQ29sb3JNYW5hZ2VyPWh9LDk2MzE6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5yZW1vdmVFbGVtZW50RnJvbVBhcmVudD12b2lkIDAsdC5yZW1vdmVFbGVtZW50RnJvbVBhcmVudD1mdW5jdGlvbigpe2Zvcih2YXIgZSx0PVtdLHI9MDtyPGFyZ3VtZW50cy5sZW5ndGg7cisrKXRbcl09YXJndW1lbnRzW3JdO2Zvcih2YXIgaT0wLG49dDtpPG4ubGVuZ3RoO2krKyl7dmFyIG89bltpXTtudWxsPT09KGU9bnVsbD09bz92b2lkIDA6by5wYXJlbnRFbGVtZW50KXx8dm9pZCAwPT09ZXx8ZS5yZW1vdmVDaGlsZChvKX19fSwzNjU2OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyPXZvaWQgMCx0LmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcj1mdW5jdGlvbihlLHQscixpKXtlLmFkZEV2ZW50TGlzdGVuZXIodCxyLGkpO3ZhciBuPSExO3JldHVybntkaXNwb3NlOmZ1bmN0aW9uKCl7bnx8KG49ITAsZS5yZW1vdmVFdmVudExpc3RlbmVyKHQscixpKSl9fX19LDM1NTE6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Nb3VzZVpvbmU9dC5MaW5raWZpZXI9dm9pZCAwO3ZhciBvPXIoODQ2MCkscz1yKDI1ODUpLGE9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyKXt0aGlzLl9idWZmZXJTZXJ2aWNlPWUsdGhpcy5fbG9nU2VydmljZT10LHRoaXMuX3VuaWNvZGVTZXJ2aWNlPXIsdGhpcy5fbGlua01hdGNoZXJzPVtdLHRoaXMuX25leHRMaW5rTWF0Y2hlcklkPTAsdGhpcy5fb25TaG93TGlua1VuZGVybGluZT1uZXcgby5FdmVudEVtaXR0ZXIsdGhpcy5fb25IaWRlTGlua1VuZGVybGluZT1uZXcgby5FdmVudEVtaXR0ZXIsdGhpcy5fb25MaW5rVG9vbHRpcD1uZXcgby5FdmVudEVtaXR0ZXIsdGhpcy5fcm93c1RvTGlua2lmeT17c3RhcnQ6dm9pZCAwLGVuZDp2b2lkIDB9fXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uU2hvd0xpbmtVbmRlcmxpbmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25TaG93TGlua1VuZGVybGluZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uSGlkZUxpbmtVbmRlcmxpbmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25IaWRlTGlua1VuZGVybGluZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uTGlua1Rvb2x0aXAiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25MaW5rVG9vbHRpcC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5hdHRhY2hUb0RvbT1mdW5jdGlvbihlLHQpe3RoaXMuX2VsZW1lbnQ9ZSx0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyPXR9LGUucHJvdG90eXBlLmxpbmtpZnlSb3dzPWZ1bmN0aW9uKHQscil7dmFyIGk9dGhpczt0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyJiYodm9pZCAwPT09dGhpcy5fcm93c1RvTGlua2lmeS5zdGFydHx8dm9pZCAwPT09dGhpcy5fcm93c1RvTGlua2lmeS5lbmQ/KHRoaXMuX3Jvd3NUb0xpbmtpZnkuc3RhcnQ9dCx0aGlzLl9yb3dzVG9MaW5raWZ5LmVuZD1yKToodGhpcy5fcm93c1RvTGlua2lmeS5zdGFydD1NYXRoLm1pbih0aGlzLl9yb3dzVG9MaW5raWZ5LnN0YXJ0LHQpLHRoaXMuX3Jvd3NUb0xpbmtpZnkuZW5kPU1hdGgubWF4KHRoaXMuX3Jvd3NUb0xpbmtpZnkuZW5kLHIpKSx0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyLmNsZWFyQWxsKHQsciksdGhpcy5fcm93c1RpbWVvdXRJZCYmY2xlYXJUaW1lb3V0KHRoaXMuX3Jvd3NUaW1lb3V0SWQpLHRoaXMuX3Jvd3NUaW1lb3V0SWQ9c2V0VGltZW91dCgoZnVuY3Rpb24oKXtyZXR1cm4gaS5fbGlua2lmeVJvd3MoKX0pLGUuX3RpbWVCZWZvcmVMYXRlbmN5KSl9LGUucHJvdG90eXBlLl9saW5raWZ5Um93cz1mdW5jdGlvbigpe3RoaXMuX3Jvd3NUaW1lb3V0SWQ9dm9pZCAwO3ZhciBlPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyO2lmKHZvaWQgMCE9PXRoaXMuX3Jvd3NUb0xpbmtpZnkuc3RhcnQmJnZvaWQgMCE9PXRoaXMuX3Jvd3NUb0xpbmtpZnkuZW5kKXt2YXIgdD1lLnlkaXNwK3RoaXMuX3Jvd3NUb0xpbmtpZnkuc3RhcnQ7aWYoISh0Pj1lLmxpbmVzLmxlbmd0aCkpe2Zvcih2YXIgcj1lLnlkaXNwK01hdGgubWluKHRoaXMuX3Jvd3NUb0xpbmtpZnkuZW5kLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cykrMSxpPU1hdGguY2VpbCgyZTMvdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSxuPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLml0ZXJhdG9yKCExLHQscixpLGkpO24uaGFzTmV4dCgpOylmb3IodmFyIG89bi5uZXh0KCkscz0wO3M8dGhpcy5fbGlua01hdGNoZXJzLmxlbmd0aDtzKyspdGhpcy5fZG9MaW5raWZ5Um93KG8ucmFuZ2UuZmlyc3Qsby5jb250ZW50LHRoaXMuX2xpbmtNYXRjaGVyc1tzXSk7dGhpcy5fcm93c1RvTGlua2lmeS5zdGFydD12b2lkIDAsdGhpcy5fcm93c1RvTGlua2lmeS5lbmQ9dm9pZCAwfX1lbHNlIHRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIl9yb3dUb0xpbmtpZnkgd2FzIHVuc2V0IGJlZm9yZSBfbGlua2lmeVJvd3Mgd2FzIGNhbGxlZCIpfSxlLnByb3RvdHlwZS5yZWdpc3RlckxpbmtNYXRjaGVyPWZ1bmN0aW9uKGUsdCxyKXtpZih2b2lkIDA9PT1yJiYocj17fSksIXQpdGhyb3cgbmV3IEVycm9yKCJoYW5kbGVyIG11c3QgYmUgZGVmaW5lZCIpO3ZhciBpPXtpZDp0aGlzLl9uZXh0TGlua01hdGNoZXJJZCsrLHJlZ2V4OmUsaGFuZGxlcjp0LG1hdGNoSW5kZXg6ci5tYXRjaEluZGV4LHZhbGlkYXRpb25DYWxsYmFjazpyLnZhbGlkYXRpb25DYWxsYmFjayxob3ZlclRvb2x0aXBDYWxsYmFjazpyLnRvb2x0aXBDYWxsYmFjayxob3ZlckxlYXZlQ2FsbGJhY2s6ci5sZWF2ZUNhbGxiYWNrLHdpbGxMaW5rQWN0aXZhdGU6ci53aWxsTGlua0FjdGl2YXRlLHByaW9yaXR5OnIucHJpb3JpdHl8fDB9O3JldHVybiB0aGlzLl9hZGRMaW5rTWF0Y2hlclRvTGlzdChpKSxpLmlkfSxlLnByb3RvdHlwZS5fYWRkTGlua01hdGNoZXJUb0xpc3Q9ZnVuY3Rpb24oZSl7aWYoMCE9PXRoaXMuX2xpbmtNYXRjaGVycy5sZW5ndGgpe2Zvcih2YXIgdD10aGlzLl9saW5rTWF0Y2hlcnMubGVuZ3RoLTE7dD49MDt0LS0paWYoZS5wcmlvcml0eTw9dGhpcy5fbGlua01hdGNoZXJzW3RdLnByaW9yaXR5KXJldHVybiB2b2lkIHRoaXMuX2xpbmtNYXRjaGVycy5zcGxpY2UodCsxLDAsZSk7dGhpcy5fbGlua01hdGNoZXJzLnNwbGljZSgwLDAsZSl9ZWxzZSB0aGlzLl9saW5rTWF0Y2hlcnMucHVzaChlKX0sZS5wcm90b3R5cGUuZGVyZWdpc3RlckxpbmtNYXRjaGVyPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8dGhpcy5fbGlua01hdGNoZXJzLmxlbmd0aDt0KyspaWYodGhpcy5fbGlua01hdGNoZXJzW3RdLmlkPT09ZSlyZXR1cm4gdGhpcy5fbGlua01hdGNoZXJzLnNwbGljZSh0LDEpLCEwO3JldHVybiExfSxlLnByb3RvdHlwZS5fZG9MaW5raWZ5Um93PWZ1bmN0aW9uKGUsdCxyKXtmb3IodmFyIGksbj10aGlzLG89bmV3IFJlZ0V4cChyLnJlZ2V4LnNvdXJjZSwoci5yZWdleC5mbGFnc3x8IiIpKyJnIikscz0tMSxhPWZ1bmN0aW9uKCl7dmFyIGE9aVsibnVtYmVyIiE9dHlwZW9mIHIubWF0Y2hJbmRleD8wOnIubWF0Y2hJbmRleF07aWYoIWEpcmV0dXJuIGMuX2xvZ1NlcnZpY2UuZGVidWcoIm1hdGNoIGZvdW5kIHdpdGhvdXQgY29ycmVzcG9uZGluZyBtYXRjaEluZGV4IixpLHIpLCJicmVhayI7aWYocz10LmluZGV4T2YoYSxzKzEpLG8ubGFzdEluZGV4PXMrYS5sZW5ndGgsczwwKXJldHVybiJicmVhayI7dmFyIGw9Yy5fYnVmZmVyU2VydmljZS5idWZmZXIuc3RyaW5nSW5kZXhUb0J1ZmZlckluZGV4KGUscyk7aWYobFswXTwwKXJldHVybiJicmVhayI7dmFyIHU9Yy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KGxbMF0pO2lmKCF1KXJldHVybiJicmVhayI7dmFyIGg9dS5nZXRGZyhsWzFdKSxmPWg/aD4+OSY1MTE6dm9pZCAwO3IudmFsaWRhdGlvbkNhbGxiYWNrP3IudmFsaWRhdGlvbkNhbGxiYWNrKGEsKGZ1bmN0aW9uKGUpe24uX3Jvd3NUaW1lb3V0SWR8fGUmJm4uX2FkZExpbmsobFsxXSxsWzBdLW4uX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwLGEscixmKX0pKTpjLl9hZGRMaW5rKGxbMV0sbFswXS1jLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCxhLHIsZil9LGM9dGhpcztudWxsIT09KGk9by5leGVjKHQpKSYmImJyZWFrIiE9PWEoKTspO30sZS5wcm90b3R5cGUuX2FkZExpbms9ZnVuY3Rpb24oZSx0LHIsaSxuKXt2YXIgbz10aGlzO2lmKHRoaXMuX21vdXNlWm9uZU1hbmFnZXImJnRoaXMuX2VsZW1lbnQpe3ZhciBzPXRoaXMuX3VuaWNvZGVTZXJ2aWNlLmdldFN0cmluZ0NlbGxXaWR0aChyKSxhPWUldGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGw9dCtNYXRoLmZsb29yKGUvdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSx1PShhK3MpJXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyxoPWwrTWF0aC5mbG9vcigoYStzKS90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpOzA9PT11JiYodT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsaC0tKSx0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyLmFkZChuZXcgYyhhKzEsbCsxLHUrMSxoKzEsKGZ1bmN0aW9uKGUpe2lmKGkuaGFuZGxlcilyZXR1cm4gaS5oYW5kbGVyKGUscik7dmFyIHQ9d2luZG93Lm9wZW4oKTt0Pyh0Lm9wZW5lcj1udWxsLHQubG9jYXRpb24uaHJlZj1yKTpjb25zb2xlLndhcm4oIk9wZW5pbmcgbGluayBibG9ja2VkIGFzIG9wZW5lciBjb3VsZCBub3QgYmUgY2xlYXJlZCIpfSksKGZ1bmN0aW9uKCl7by5fb25TaG93TGlua1VuZGVybGluZS5maXJlKG8uX2NyZWF0ZUxpbmtIb3ZlckV2ZW50KGEsbCx1LGgsbikpLG8uX2VsZW1lbnQuY2xhc3NMaXN0LmFkZCgieHRlcm0tY3Vyc29yLXBvaW50ZXIiKX0pLChmdW5jdGlvbihlKXtvLl9vbkxpbmtUb29sdGlwLmZpcmUoby5fY3JlYXRlTGlua0hvdmVyRXZlbnQoYSxsLHUsaCxuKSksaS5ob3ZlclRvb2x0aXBDYWxsYmFjayYmaS5ob3ZlclRvb2x0aXBDYWxsYmFjayhlLHIse3N0YXJ0Ont4OmEseTpsfSxlbmQ6e3g6dSx5Omh9fSl9KSwoZnVuY3Rpb24oKXtvLl9vbkhpZGVMaW5rVW5kZXJsaW5lLmZpcmUoby5fY3JlYXRlTGlua0hvdmVyRXZlbnQoYSxsLHUsaCxuKSksby5fZWxlbWVudC5jbGFzc0xpc3QucmVtb3ZlKCJ4dGVybS1jdXJzb3ItcG9pbnRlciIpLGkuaG92ZXJMZWF2ZUNhbGxiYWNrJiZpLmhvdmVyTGVhdmVDYWxsYmFjaygpfSksKGZ1bmN0aW9uKGUpe3JldHVybiFpLndpbGxMaW5rQWN0aXZhdGV8fGkud2lsbExpbmtBY3RpdmF0ZShlLHIpfSkpKX19LGUucHJvdG90eXBlLl9jcmVhdGVMaW5rSG92ZXJFdmVudD1mdW5jdGlvbihlLHQscixpLG4pe3JldHVybnt4MTplLHkxOnQseDI6cix5MjppLGNvbHM6dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGZnOm59fSxlLl90aW1lQmVmb3JlTGF0ZW5jeT0yMDAsZT1pKFtuKDAscy5JQnVmZmVyU2VydmljZSksbigxLHMuSUxvZ1NlcnZpY2UpLG4oMixzLklVbmljb2RlU2VydmljZSldLGUpfSgpO3QuTGlua2lmaWVyPWE7dmFyIGM9ZnVuY3Rpb24oZSx0LHIsaSxuLG8scyxhLGMpe3RoaXMueDE9ZSx0aGlzLnkxPXQsdGhpcy54Mj1yLHRoaXMueTI9aSx0aGlzLmNsaWNrQ2FsbGJhY2s9bix0aGlzLmhvdmVyQ2FsbGJhY2s9byx0aGlzLnRvb2x0aXBDYWxsYmFjaz1zLHRoaXMubGVhdmVDYWxsYmFjaz1hLHRoaXMud2lsbExpbmtBY3RpdmF0ZT1jfTt0Lk1vdXNlWm9uZT1jfSw2NDY1OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkxpbmtpZmllcjI9dm9pZCAwO3ZhciBhPXIoMjU4NSksYz1yKDg0NjApLGw9cig4NDQpLHU9cigzNjU2KSxoPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCl7dmFyIHI9ZS5jYWxsKHRoaXMpfHx0aGlzO3JldHVybiByLl9idWZmZXJTZXJ2aWNlPXQsci5fbGlua1Byb3ZpZGVycz1bXSxyLl9saW5rQ2FjaGVEaXNwb3NhYmxlcz1bXSxyLl9pc01vdXNlT3V0PSEwLHIuX2FjdGl2ZUxpbmU9LTEsci5fb25TaG93TGlua1VuZGVybGluZT1yLnJlZ2lzdGVyKG5ldyBjLkV2ZW50RW1pdHRlciksci5fb25IaWRlTGlua1VuZGVybGluZT1yLnJlZ2lzdGVyKG5ldyBjLkV2ZW50RW1pdHRlciksci5yZWdpc3RlcigoMCxsLmdldERpc3Bvc2VBcnJheURpc3Bvc2FibGUpKHIuX2xpbmtDYWNoZURpc3Bvc2FibGVzKSkscn1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwiY3VycmVudExpbmsiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY3VycmVudExpbmt9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblNob3dMaW5rVW5kZXJsaW5lIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uU2hvd0xpbmtVbmRlcmxpbmUuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkhpZGVMaW5rVW5kZXJsaW5lIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uSGlkZUxpbmtVbmRlcmxpbmUuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUucmVnaXN0ZXJMaW5rUHJvdmlkZXI9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcztyZXR1cm4gdGhpcy5fbGlua1Byb3ZpZGVycy5wdXNoKGUpLHtkaXNwb3NlOmZ1bmN0aW9uKCl7dmFyIHI9dC5fbGlua1Byb3ZpZGVycy5pbmRleE9mKGUpOy0xIT09ciYmdC5fbGlua1Byb3ZpZGVycy5zcGxpY2UociwxKX19fSx0LnByb3RvdHlwZS5hdHRhY2hUb0RvbT1mdW5jdGlvbihlLHQscil7dmFyIGk9dGhpczt0aGlzLl9lbGVtZW50PWUsdGhpcy5fbW91c2VTZXJ2aWNlPXQsdGhpcy5fcmVuZGVyU2VydmljZT1yLHRoaXMucmVnaXN0ZXIoKDAsdS5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMuX2VsZW1lbnQsIm1vdXNlbGVhdmUiLChmdW5jdGlvbigpe2kuX2lzTW91c2VPdXQ9ITAsaS5fY2xlYXJDdXJyZW50TGluaygpfSkpKSx0aGlzLnJlZ2lzdGVyKCgwLHUuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLl9lbGVtZW50LCJtb3VzZW1vdmUiLHRoaXMuX29uTW91c2VNb3ZlLmJpbmQodGhpcykpKSx0aGlzLnJlZ2lzdGVyKCgwLHUuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLl9lbGVtZW50LCJjbGljayIsdGhpcy5fb25DbGljay5iaW5kKHRoaXMpKSl9LHQucHJvdG90eXBlLl9vbk1vdXNlTW92ZT1mdW5jdGlvbihlKXtpZih0aGlzLl9sYXN0TW91c2VFdmVudD1lLHRoaXMuX2VsZW1lbnQmJnRoaXMuX21vdXNlU2VydmljZSl7dmFyIHQ9dGhpcy5fcG9zaXRpb25Gcm9tTW91c2VFdmVudChlLHRoaXMuX2VsZW1lbnQsdGhpcy5fbW91c2VTZXJ2aWNlKTtpZih0KXt0aGlzLl9pc01vdXNlT3V0PSExO2Zvcih2YXIgcj1lLmNvbXBvc2VkUGF0aCgpLGk9MDtpPHIubGVuZ3RoO2krKyl7dmFyIG49cltpXTtpZihuLmNsYXNzTGlzdC5jb250YWlucygieHRlcm0iKSlicmVhaztpZihuLmNsYXNzTGlzdC5jb250YWlucygieHRlcm0taG92ZXIiKSlyZXR1cm59dGhpcy5fbGFzdEJ1ZmZlckNlbGwmJnQueD09PXRoaXMuX2xhc3RCdWZmZXJDZWxsLngmJnQueT09PXRoaXMuX2xhc3RCdWZmZXJDZWxsLnl8fCh0aGlzLl9vbkhvdmVyKHQpLHRoaXMuX2xhc3RCdWZmZXJDZWxsPXQpfX19LHQucHJvdG90eXBlLl9vbkhvdmVyPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUxpbmUhPT1lLnkpcmV0dXJuIHRoaXMuX2NsZWFyQ3VycmVudExpbmsoKSx2b2lkIHRoaXMuX2Fza0ZvckxpbmsoZSwhMSk7dGhpcy5fY3VycmVudExpbmsmJnRoaXMuX2xpbmtBdFBvc2l0aW9uKHRoaXMuX2N1cnJlbnRMaW5rLmxpbmssZSl8fCh0aGlzLl9jbGVhckN1cnJlbnRMaW5rKCksdGhpcy5fYXNrRm9yTGluayhlLCEwKSl9LHQucHJvdG90eXBlLl9hc2tGb3JMaW5rPWZ1bmN0aW9uKGUsdCl7dmFyIHIsaT10aGlzO3RoaXMuX2FjdGl2ZVByb3ZpZGVyUmVwbGllcyYmdHx8KG51bGw9PT0ocj10aGlzLl9hY3RpdmVQcm92aWRlclJlcGxpZXMpfHx2b2lkIDA9PT1yfHxyLmZvckVhY2goKGZ1bmN0aW9uKGUpe251bGw9PWV8fGUuZm9yRWFjaCgoZnVuY3Rpb24oZSl7ZS5saW5rLmRpc3Bvc2UmJmUubGluay5kaXNwb3NlKCl9KSl9KSksdGhpcy5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzPW5ldyBNYXAsdGhpcy5fYWN0aXZlTGluZT1lLnkpO3ZhciBuPSExO3RoaXMuX2xpbmtQcm92aWRlcnMuZm9yRWFjaCgoZnVuY3Rpb24ocixvKXt2YXIgczt0PyhudWxsPT09KHM9aS5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzKXx8dm9pZCAwPT09cz92b2lkIDA6cy5nZXQobykpJiYobj1pLl9jaGVja0xpbmtQcm92aWRlclJlc3VsdChvLGUsbikpOnIucHJvdmlkZUxpbmtzKGUueSwoZnVuY3Rpb24odCl7dmFyIHIscztpZighaS5faXNNb3VzZU91dCl7dmFyIGE9bnVsbD09dD92b2lkIDA6dC5tYXAoKGZ1bmN0aW9uKGUpe3JldHVybntsaW5rOmV9fSkpO251bGw9PT0ocj1pLl9hY3RpdmVQcm92aWRlclJlcGxpZXMpfHx2b2lkIDA9PT1yfHxyLnNldChvLGEpLG49aS5fY2hlY2tMaW5rUHJvdmlkZXJSZXN1bHQobyxlLG4pLChudWxsPT09KHM9aS5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzKXx8dm9pZCAwPT09cz92b2lkIDA6cy5zaXplKT09PWkuX2xpbmtQcm92aWRlcnMubGVuZ3RoJiZpLl9yZW1vdmVJbnRlcnNlY3RpbmdMaW5rcyhlLnksaS5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzKX19KSl9KSl9LHQucHJvdG90eXBlLl9yZW1vdmVJbnRlcnNlY3RpbmdMaW5rcz1mdW5jdGlvbihlLHQpe2Zvcih2YXIgcj1uZXcgU2V0LGk9MDtpPHQuc2l6ZTtpKyspe3ZhciBuPXQuZ2V0KGkpO2lmKG4pZm9yKHZhciBvPTA7bzxuLmxlbmd0aDtvKyspZm9yKHZhciBzPW5bb10sYT1zLmxpbmsucmFuZ2Uuc3RhcnQueTxlPzA6cy5saW5rLnJhbmdlLnN0YXJ0LngsYz1zLmxpbmsucmFuZ2UuZW5kLnk+ZT90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM6cy5saW5rLnJhbmdlLmVuZC54LGw9YTtsPD1jO2wrKyl7aWYoci5oYXMobCkpe24uc3BsaWNlKG8tLSwxKTticmVha31yLmFkZChsKX19fSx0LnByb3RvdHlwZS5fY2hlY2tMaW5rUHJvdmlkZXJSZXN1bHQ9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcztpZighdGhpcy5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzKXJldHVybiByO2Zvcih2YXIgbz10aGlzLl9hY3RpdmVQcm92aWRlclJlcGxpZXMuZ2V0KGUpLHM9ITEsYT0wO2E8ZTthKyspdGhpcy5fYWN0aXZlUHJvdmlkZXJSZXBsaWVzLmhhcyhhKSYmIXRoaXMuX2FjdGl2ZVByb3ZpZGVyUmVwbGllcy5nZXQoYSl8fChzPSEwKTtpZighcyYmbyl7dmFyIGM9by5maW5kKChmdW5jdGlvbihlKXtyZXR1cm4gbi5fbGlua0F0UG9zaXRpb24oZS5saW5rLHQpfSkpO2MmJihyPSEwLHRoaXMuX2hhbmRsZU5ld0xpbmsoYykpfWlmKHRoaXMuX2FjdGl2ZVByb3ZpZGVyUmVwbGllcy5zaXplPT09dGhpcy5fbGlua1Byb3ZpZGVycy5sZW5ndGgmJiFyKWZvcihhPTA7YTx0aGlzLl9hY3RpdmVQcm92aWRlclJlcGxpZXMuc2l6ZTthKyspe3ZhciBsPW51bGw9PT0oaT10aGlzLl9hY3RpdmVQcm92aWRlclJlcGxpZXMuZ2V0KGEpKXx8dm9pZCAwPT09aT92b2lkIDA6aS5maW5kKChmdW5jdGlvbihlKXtyZXR1cm4gbi5fbGlua0F0UG9zaXRpb24oZS5saW5rLHQpfSkpO2lmKGwpe3I9ITAsdGhpcy5faGFuZGxlTmV3TGluayhsKTticmVha319cmV0dXJuIHJ9LHQucHJvdG90eXBlLl9vbkNsaWNrPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2VsZW1lbnQmJnRoaXMuX21vdXNlU2VydmljZSYmdGhpcy5fY3VycmVudExpbmspe3ZhciB0PXRoaXMuX3Bvc2l0aW9uRnJvbU1vdXNlRXZlbnQoZSx0aGlzLl9lbGVtZW50LHRoaXMuX21vdXNlU2VydmljZSk7dCYmdGhpcy5fbGlua0F0UG9zaXRpb24odGhpcy5fY3VycmVudExpbmsubGluayx0KSYmdGhpcy5fY3VycmVudExpbmsubGluay5hY3RpdmF0ZShlLHRoaXMuX2N1cnJlbnRMaW5rLmxpbmsudGV4dCl9fSx0LnByb3RvdHlwZS5fY2xlYXJDdXJyZW50TGluaz1mdW5jdGlvbihlLHQpe3RoaXMuX2VsZW1lbnQmJnRoaXMuX2N1cnJlbnRMaW5rJiZ0aGlzLl9sYXN0TW91c2VFdmVudCYmKCFlfHwhdHx8dGhpcy5fY3VycmVudExpbmsubGluay5yYW5nZS5zdGFydC55Pj1lJiZ0aGlzLl9jdXJyZW50TGluay5saW5rLnJhbmdlLmVuZC55PD10KSYmKHRoaXMuX2xpbmtMZWF2ZSh0aGlzLl9lbGVtZW50LHRoaXMuX2N1cnJlbnRMaW5rLmxpbmssdGhpcy5fbGFzdE1vdXNlRXZlbnQpLHRoaXMuX2N1cnJlbnRMaW5rPXZvaWQgMCwoMCxsLmRpc3Bvc2VBcnJheSkodGhpcy5fbGlua0NhY2hlRGlzcG9zYWJsZXMpKX0sdC5wcm90b3R5cGUuX2hhbmRsZU5ld0xpbms9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcztpZih0aGlzLl9lbGVtZW50JiZ0aGlzLl9sYXN0TW91c2VFdmVudCYmdGhpcy5fbW91c2VTZXJ2aWNlKXt2YXIgcj10aGlzLl9wb3NpdGlvbkZyb21Nb3VzZUV2ZW50KHRoaXMuX2xhc3RNb3VzZUV2ZW50LHRoaXMuX2VsZW1lbnQsdGhpcy5fbW91c2VTZXJ2aWNlKTtyJiZ0aGlzLl9saW5rQXRQb3NpdGlvbihlLmxpbmsscikmJih0aGlzLl9jdXJyZW50TGluaz1lLHRoaXMuX2N1cnJlbnRMaW5rLnN0YXRlPXtkZWNvcmF0aW9uczp7dW5kZXJsaW5lOnZvaWQgMD09PWUubGluay5kZWNvcmF0aW9uc3x8ZS5saW5rLmRlY29yYXRpb25zLnVuZGVybGluZSxwb2ludGVyQ3Vyc29yOnZvaWQgMD09PWUubGluay5kZWNvcmF0aW9uc3x8ZS5saW5rLmRlY29yYXRpb25zLnBvaW50ZXJDdXJzb3J9LGlzSG92ZXJlZDohMH0sdGhpcy5fbGlua0hvdmVyKHRoaXMuX2VsZW1lbnQsZS5saW5rLHRoaXMuX2xhc3RNb3VzZUV2ZW50KSxlLmxpbmsuZGVjb3JhdGlvbnM9e30sT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoZS5saW5rLmRlY29yYXRpb25zLHtwb2ludGVyQ3Vyc29yOntnZXQ6ZnVuY3Rpb24oKXt2YXIgZSxyO3JldHVybiBudWxsPT09KHI9bnVsbD09PShlPXQuX2N1cnJlbnRMaW5rKXx8dm9pZCAwPT09ZT92b2lkIDA6ZS5zdGF0ZSl8fHZvaWQgMD09PXI/dm9pZCAwOnIuZGVjb3JhdGlvbnMucG9pbnRlckN1cnNvcn0sc2V0OmZ1bmN0aW9uKGUpe3ZhciByLGk7KG51bGw9PT0ocj10Ll9jdXJyZW50TGluayl8fHZvaWQgMD09PXI/dm9pZCAwOnIuc3RhdGUpJiZ0Ll9jdXJyZW50TGluay5zdGF0ZS5kZWNvcmF0aW9ucy5wb2ludGVyQ3Vyc29yIT09ZSYmKHQuX2N1cnJlbnRMaW5rLnN0YXRlLmRlY29yYXRpb25zLnBvaW50ZXJDdXJzb3I9ZSx0Ll9jdXJyZW50TGluay5zdGF0ZS5pc0hvdmVyZWQmJihudWxsPT09KGk9dC5fZWxlbWVudCl8fHZvaWQgMD09PWl8fGkuY2xhc3NMaXN0LnRvZ2dsZSgieHRlcm0tY3Vyc29yLXBvaW50ZXIiLGUpKSl9fSx1bmRlcmxpbmU6e2dldDpmdW5jdGlvbigpe3ZhciBlLHI7cmV0dXJuIG51bGw9PT0ocj1udWxsPT09KGU9dC5fY3VycmVudExpbmspfHx2b2lkIDA9PT1lP3ZvaWQgMDplLnN0YXRlKXx8dm9pZCAwPT09cj92b2lkIDA6ci5kZWNvcmF0aW9ucy51bmRlcmxpbmV9LHNldDpmdW5jdGlvbihyKXt2YXIgaSxuLG87KG51bGw9PT0oaT10Ll9jdXJyZW50TGluayl8fHZvaWQgMD09PWk/dm9pZCAwOmkuc3RhdGUpJiYobnVsbD09PShvPW51bGw9PT0obj10Ll9jdXJyZW50TGluayl8fHZvaWQgMD09PW4/dm9pZCAwOm4uc3RhdGUpfHx2b2lkIDA9PT1vP3ZvaWQgMDpvLmRlY29yYXRpb25zLnVuZGVybGluZSkhPT1yJiYodC5fY3VycmVudExpbmsuc3RhdGUuZGVjb3JhdGlvbnMudW5kZXJsaW5lPXIsdC5fY3VycmVudExpbmsuc3RhdGUuaXNIb3ZlcmVkJiZ0Ll9maXJlVW5kZXJsaW5lRXZlbnQoZS5saW5rLHIpKX19fSksdGhpcy5fcmVuZGVyU2VydmljZSYmdGhpcy5fbGlua0NhY2hlRGlzcG9zYWJsZXMucHVzaCh0aGlzLl9yZW5kZXJTZXJ2aWNlLm9uUmVuZGVyZWRCdWZmZXJDaGFuZ2UoKGZ1bmN0aW9uKGUpe3ZhciByPTA9PT1lLnN0YXJ0PzA6ZS5zdGFydCsxK3QuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwO3QuX2NsZWFyQ3VycmVudExpbmsocixlLmVuZCsxK3QuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwKX0pKSkpfX0sdC5wcm90b3R5cGUuX2xpbmtIb3Zlcj1mdW5jdGlvbihlLHQscil7dmFyIGk7KG51bGw9PT0oaT10aGlzLl9jdXJyZW50TGluayl8fHZvaWQgMD09PWk/dm9pZCAwOmkuc3RhdGUpJiYodGhpcy5fY3VycmVudExpbmsuc3RhdGUuaXNIb3ZlcmVkPSEwLHRoaXMuX2N1cnJlbnRMaW5rLnN0YXRlLmRlY29yYXRpb25zLnVuZGVybGluZSYmdGhpcy5fZmlyZVVuZGVybGluZUV2ZW50KHQsITApLHRoaXMuX2N1cnJlbnRMaW5rLnN0YXRlLmRlY29yYXRpb25zLnBvaW50ZXJDdXJzb3ImJmUuY2xhc3NMaXN0LmFkZCgieHRlcm0tY3Vyc29yLXBvaW50ZXIiKSksdC5ob3ZlciYmdC5ob3ZlcihyLHQudGV4dCl9LHQucHJvdG90eXBlLl9maXJlVW5kZXJsaW5lRXZlbnQ9ZnVuY3Rpb24oZSx0KXt2YXIgcj1lLnJhbmdlLGk9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3Asbj10aGlzLl9jcmVhdGVMaW5rVW5kZXJsaW5lRXZlbnQoci5zdGFydC54LTEsci5zdGFydC55LWktMSxyLmVuZC54LHIuZW5kLnktaS0xLHZvaWQgMCk7KHQ/dGhpcy5fb25TaG93TGlua1VuZGVybGluZTp0aGlzLl9vbkhpZGVMaW5rVW5kZXJsaW5lKS5maXJlKG4pfSx0LnByb3RvdHlwZS5fbGlua0xlYXZlPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaTsobnVsbD09PShpPXRoaXMuX2N1cnJlbnRMaW5rKXx8dm9pZCAwPT09aT92b2lkIDA6aS5zdGF0ZSkmJih0aGlzLl9jdXJyZW50TGluay5zdGF0ZS5pc0hvdmVyZWQ9ITEsdGhpcy5fY3VycmVudExpbmsuc3RhdGUuZGVjb3JhdGlvbnMudW5kZXJsaW5lJiZ0aGlzLl9maXJlVW5kZXJsaW5lRXZlbnQodCwhMSksdGhpcy5fY3VycmVudExpbmsuc3RhdGUuZGVjb3JhdGlvbnMucG9pbnRlckN1cnNvciYmZS5jbGFzc0xpc3QucmVtb3ZlKCJ4dGVybS1jdXJzb3ItcG9pbnRlciIpKSx0LmxlYXZlJiZ0LmxlYXZlKHIsdC50ZXh0KX0sdC5wcm90b3R5cGUuX2xpbmtBdFBvc2l0aW9uPWZ1bmN0aW9uKGUsdCl7dmFyIHI9ZS5yYW5nZS5zdGFydC55PT09ZS5yYW5nZS5lbmQueSxpPWUucmFuZ2Uuc3RhcnQueTx0Lnksbj1lLnJhbmdlLmVuZC55PnQueTtyZXR1cm4ociYmZS5yYW5nZS5zdGFydC54PD10LngmJmUucmFuZ2UuZW5kLng+PXQueHx8aSYmZS5yYW5nZS5lbmQueD49dC54fHxuJiZlLnJhbmdlLnN0YXJ0Lng8PXQueHx8aSYmbikmJmUucmFuZ2Uuc3RhcnQueTw9dC55JiZlLnJhbmdlLmVuZC55Pj10Lnl9LHQucHJvdG90eXBlLl9wb3NpdGlvbkZyb21Nb3VzZUV2ZW50PWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT1yLmdldENvb3JkcyhlLHQsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyk7aWYoaSlyZXR1cm57eDppWzBdLHk6aVsxXSt0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcH19LHQucHJvdG90eXBlLl9jcmVhdGVMaW5rVW5kZXJsaW5lRXZlbnQ9ZnVuY3Rpb24oZSx0LHIsaSxuKXtyZXR1cm57eDE6ZSx5MTp0LHgyOnIseTI6aSxjb2xzOnRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyxmZzpufX0sbyhbcygwLGEuSUJ1ZmZlclNlcnZpY2UpXSx0KX0obC5EaXNwb3NhYmxlKTt0LkxpbmtpZmllcjI9aH0sOTA0MjooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LnRvb011Y2hPdXRwdXQ9dC5wcm9tcHRMYWJlbD12b2lkIDAsdC5wcm9tcHRMYWJlbD0iVGVybWluYWwgaW5wdXQiLHQudG9vTXVjaE91dHB1dD0iVG9vIG11Y2ggb3V0cHV0IHRvIGFubm91bmNlLCBuYXZpZ2F0ZSB0byByb3dzIG1hbnVhbGx5IHRvIHJlYWQifSw2OTU0OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0Lk1vdXNlWm9uZU1hbmFnZXI9dm9pZCAwO3ZhciBhPXIoODQ0KSxjPXIoMzY1NiksbD1yKDQ3MjUpLHU9cigyNTg1KSxoPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyLGksbixvLHMpe3ZhciBhPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gYS5fZWxlbWVudD10LGEuX3NjcmVlbkVsZW1lbnQ9cixhLl9idWZmZXJTZXJ2aWNlPWksYS5fbW91c2VTZXJ2aWNlPW4sYS5fc2VsZWN0aW9uU2VydmljZT1vLGEuX29wdGlvbnNTZXJ2aWNlPXMsYS5fem9uZXM9W10sYS5fYXJlWm9uZXNBY3RpdmU9ITEsYS5fbGFzdEhvdmVyQ29vcmRzPVt2b2lkIDAsdm9pZCAwXSxhLl9pbml0aWFsU2VsZWN0aW9uTGVuZ3RoPTAsYS5yZWdpc3RlcigoMCxjLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikoYS5fZWxlbWVudCwibW91c2Vkb3duIiwoZnVuY3Rpb24oZSl7cmV0dXJuIGEuX29uTW91c2VEb3duKGUpfSkpKSxhLl9tb3VzZU1vdmVMaXN0ZW5lcj1mdW5jdGlvbihlKXtyZXR1cm4gYS5fb25Nb3VzZU1vdmUoZSl9LGEuX21vdXNlTGVhdmVMaXN0ZW5lcj1mdW5jdGlvbihlKXtyZXR1cm4gYS5fb25Nb3VzZUxlYXZlKGUpfSxhLl9jbGlja0xpc3RlbmVyPWZ1bmN0aW9uKGUpe3JldHVybiBhLl9vbkNsaWNrKGUpfSxhfXJldHVybiBuKHQsZSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe2UucHJvdG90eXBlLmRpc3Bvc2UuY2FsbCh0aGlzKSx0aGlzLl9kZWFjdGl2YXRlKCl9LHQucHJvdG90eXBlLmFkZD1mdW5jdGlvbihlKXt0aGlzLl96b25lcy5wdXNoKGUpLDE9PT10aGlzLl96b25lcy5sZW5ndGgmJnRoaXMuX2FjdGl2YXRlKCl9LHQucHJvdG90eXBlLmNsZWFyQWxsPWZ1bmN0aW9uKGUsdCl7aWYoMCE9PXRoaXMuX3pvbmVzLmxlbmd0aCl7ZSYmdHx8KGU9MCx0PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xKTtmb3IodmFyIHI9MDtyPHRoaXMuX3pvbmVzLmxlbmd0aDtyKyspe3ZhciBpPXRoaXMuX3pvbmVzW3JdOyhpLnkxPmUmJmkueTE8PXQrMXx8aS55Mj5lJiZpLnkyPD10KzF8fGkueTE8ZSYmaS55Mj50KzEpJiYodGhpcy5fY3VycmVudFpvbmUmJnRoaXMuX2N1cnJlbnRab25lPT09aSYmKHRoaXMuX2N1cnJlbnRab25lLmxlYXZlQ2FsbGJhY2soKSx0aGlzLl9jdXJyZW50Wm9uZT12b2lkIDApLHRoaXMuX3pvbmVzLnNwbGljZShyLS0sMSkpfTA9PT10aGlzLl96b25lcy5sZW5ndGgmJnRoaXMuX2RlYWN0aXZhdGUoKX19LHQucHJvdG90eXBlLl9hY3RpdmF0ZT1mdW5jdGlvbigpe3RoaXMuX2FyZVpvbmVzQWN0aXZlfHwodGhpcy5fYXJlWm9uZXNBY3RpdmU9ITAsdGhpcy5fZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCJtb3VzZW1vdmUiLHRoaXMuX21vdXNlTW92ZUxpc3RlbmVyKSx0aGlzLl9lbGVtZW50LmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNlbGVhdmUiLHRoaXMuX21vdXNlTGVhdmVMaXN0ZW5lciksdGhpcy5fZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsdGhpcy5fY2xpY2tMaXN0ZW5lcikpfSx0LnByb3RvdHlwZS5fZGVhY3RpdmF0ZT1mdW5jdGlvbigpe3RoaXMuX2FyZVpvbmVzQWN0aXZlJiYodGhpcy5fYXJlWm9uZXNBY3RpdmU9ITEsdGhpcy5fZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCJtb3VzZW1vdmUiLHRoaXMuX21vdXNlTW92ZUxpc3RlbmVyKSx0aGlzLl9lbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoIm1vdXNlbGVhdmUiLHRoaXMuX21vdXNlTGVhdmVMaXN0ZW5lciksdGhpcy5fZWxlbWVudC5yZW1vdmVFdmVudExpc3RlbmVyKCJjbGljayIsdGhpcy5fY2xpY2tMaXN0ZW5lcikpfSx0LnByb3RvdHlwZS5fb25Nb3VzZU1vdmU9ZnVuY3Rpb24oZSl7dGhpcy5fbGFzdEhvdmVyQ29vcmRzWzBdPT09ZS5wYWdlWCYmdGhpcy5fbGFzdEhvdmVyQ29vcmRzWzFdPT09ZS5wYWdlWXx8KHRoaXMuX29uSG92ZXIoZSksdGhpcy5fbGFzdEhvdmVyQ29vcmRzPVtlLnBhZ2VYLGUucGFnZVldKX0sdC5wcm90b3R5cGUuX29uSG92ZXI9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxyPXRoaXMuX2ZpbmRab25lRXZlbnRBdChlKTtyIT09dGhpcy5fY3VycmVudFpvbmUmJih0aGlzLl9jdXJyZW50Wm9uZSYmKHRoaXMuX2N1cnJlbnRab25lLmxlYXZlQ2FsbGJhY2soKSx0aGlzLl9jdXJyZW50Wm9uZT12b2lkIDAsdGhpcy5fdG9vbHRpcFRpbWVvdXQmJmNsZWFyVGltZW91dCh0aGlzLl90b29sdGlwVGltZW91dCkpLHImJih0aGlzLl9jdXJyZW50Wm9uZT1yLHIuaG92ZXJDYWxsYmFjayYmci5ob3ZlckNhbGxiYWNrKGUpLHRoaXMuX3Rvb2x0aXBUaW1lb3V0PXdpbmRvdy5zZXRUaW1lb3V0KChmdW5jdGlvbigpe3JldHVybiB0Ll9vblRvb2x0aXAoZSl9KSx0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmxpbmtUb29sdGlwSG92ZXJEdXJhdGlvbikpKX0sdC5wcm90b3R5cGUuX29uVG9vbHRpcD1mdW5jdGlvbihlKXt0aGlzLl90b29sdGlwVGltZW91dD12b2lkIDA7dmFyIHQ9dGhpcy5fZmluZFpvbmVFdmVudEF0KGUpO251bGw9PXR8fHQudG9vbHRpcENhbGxiYWNrKGUpfSx0LnByb3RvdHlwZS5fb25Nb3VzZURvd249ZnVuY3Rpb24oZSl7aWYodGhpcy5faW5pdGlhbFNlbGVjdGlvbkxlbmd0aD10aGlzLl9nZXRTZWxlY3Rpb25MZW5ndGgoKSx0aGlzLl9hcmVab25lc0FjdGl2ZSl7dmFyIHQ9dGhpcy5fZmluZFpvbmVFdmVudEF0KGUpOyhudWxsPT10P3ZvaWQgMDp0LndpbGxMaW5rQWN0aXZhdGUoZSkpJiYoZS5wcmV2ZW50RGVmYXVsdCgpLGUuc3RvcEltbWVkaWF0ZVByb3BhZ2F0aW9uKCkpfX0sdC5wcm90b3R5cGUuX29uTW91c2VMZWF2ZT1mdW5jdGlvbihlKXt0aGlzLl9jdXJyZW50Wm9uZSYmKHRoaXMuX2N1cnJlbnRab25lLmxlYXZlQ2FsbGJhY2soKSx0aGlzLl9jdXJyZW50Wm9uZT12b2lkIDAsdGhpcy5fdG9vbHRpcFRpbWVvdXQmJmNsZWFyVGltZW91dCh0aGlzLl90b29sdGlwVGltZW91dCkpfSx0LnByb3RvdHlwZS5fb25DbGljaz1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9maW5kWm9uZUV2ZW50QXQoZSkscj10aGlzLl9nZXRTZWxlY3Rpb25MZW5ndGgoKTt0JiZyPT09dGhpcy5faW5pdGlhbFNlbGVjdGlvbkxlbmd0aCYmKHQuY2xpY2tDYWxsYmFjayhlKSxlLnByZXZlbnREZWZhdWx0KCksZS5zdG9wSW1tZWRpYXRlUHJvcGFnYXRpb24oKSl9LHQucHJvdG90eXBlLl9nZXRTZWxlY3Rpb25MZW5ndGg9ZnVuY3Rpb24oKXt2YXIgZT10aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLnNlbGVjdGlvblRleHQ7cmV0dXJuIGU/ZS5sZW5ndGg6MH0sdC5wcm90b3R5cGUuX2ZpbmRab25lRXZlbnRBdD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9tb3VzZVNlcnZpY2UuZ2V0Q29vcmRzKGUsdGhpcy5fc2NyZWVuRWxlbWVudCx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKTtpZih0KWZvcih2YXIgcj10WzBdLGk9dFsxXSxuPTA7bjx0aGlzLl96b25lcy5sZW5ndGg7bisrKXt2YXIgbz10aGlzLl96b25lc1tuXTtpZihvLnkxPT09by55Mil7aWYoaT09PW8ueTEmJnI+PW8ueDEmJnI8by54MilyZXR1cm4gb31lbHNlIGlmKGk9PT1vLnkxJiZyPj1vLngxfHxpPT09by55MiYmcjxvLngyfHxpPm8ueTEmJmk8by55MilyZXR1cm4gb319LG8oW3MoMix1LklCdWZmZXJTZXJ2aWNlKSxzKDMsbC5JTW91c2VTZXJ2aWNlKSxzKDQsbC5JU2VsZWN0aW9uU2VydmljZSkscyg1LHUuSU9wdGlvbnNTZXJ2aWNlKV0sdCl9KGEuRGlzcG9zYWJsZSk7dC5Nb3VzZVpvbmVNYW5hZ2VyPWh9LDYxOTM6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5SZW5kZXJEZWJvdW5jZXI9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9yZW5kZXJDYWxsYmFjaz1lfXJldHVybiBlLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5fYW5pbWF0aW9uRnJhbWUmJih3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUodGhpcy5fYW5pbWF0aW9uRnJhbWUpLHRoaXMuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMCl9LGUucHJvdG90eXBlLnJlZnJlc2g9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXM7dGhpcy5fcm93Q291bnQ9cixlPXZvaWQgMCE9PWU/ZTowLHQ9dm9pZCAwIT09dD90OnRoaXMuX3Jvd0NvdW50LTEsdGhpcy5fcm93U3RhcnQ9dm9pZCAwIT09dGhpcy5fcm93U3RhcnQ/TWF0aC5taW4odGhpcy5fcm93U3RhcnQsZSk6ZSx0aGlzLl9yb3dFbmQ9dm9pZCAwIT09dGhpcy5fcm93RW5kP01hdGgubWF4KHRoaXMuX3Jvd0VuZCx0KTp0LHRoaXMuX2FuaW1hdGlvbkZyYW1lfHwodGhpcy5fYW5pbWF0aW9uRnJhbWU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoZnVuY3Rpb24oKXtyZXR1cm4gaS5faW5uZXJSZWZyZXNoKCl9KSkpfSxlLnByb3RvdHlwZS5faW5uZXJSZWZyZXNoPWZ1bmN0aW9uKCl7aWYodm9pZCAwIT09dGhpcy5fcm93U3RhcnQmJnZvaWQgMCE9PXRoaXMuX3Jvd0VuZCYmdm9pZCAwIT09dGhpcy5fcm93Q291bnQpe3ZhciBlPU1hdGgubWF4KHRoaXMuX3Jvd1N0YXJ0LDApLHQ9TWF0aC5taW4odGhpcy5fcm93RW5kLHRoaXMuX3Jvd0NvdW50LTEpO3RoaXMuX3Jvd1N0YXJ0PXZvaWQgMCx0aGlzLl9yb3dFbmQ9dm9pZCAwLHRoaXMuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMCx0aGlzLl9yZW5kZXJDYWxsYmFjayhlLHQpfX0sZX0oKTt0LlJlbmRlckRlYm91bmNlcj1yfSw1NTk2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlNjcmVlbkRwck1vbml0b3I9dm9pZCAwO3ZhciBvPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQoKXt2YXIgdD1udWxsIT09ZSYmZS5hcHBseSh0aGlzLGFyZ3VtZW50cyl8fHRoaXM7cmV0dXJuIHQuX2N1cnJlbnREZXZpY2VQaXhlbFJhdGlvPXdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHR9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5zZXRMaXN0ZW5lcj1mdW5jdGlvbihlKXt2YXIgdD10aGlzO3RoaXMuX2xpc3RlbmVyJiZ0aGlzLmNsZWFyTGlzdGVuZXIoKSx0aGlzLl9saXN0ZW5lcj1lLHRoaXMuX291dGVyTGlzdGVuZXI9ZnVuY3Rpb24oKXt0Ll9saXN0ZW5lciYmKHQuX2xpc3RlbmVyKHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHQuX2N1cnJlbnREZXZpY2VQaXhlbFJhdGlvKSx0Ll91cGRhdGVEcHIoKSl9LHRoaXMuX3VwZGF0ZURwcigpfSx0LnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7ZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpLHRoaXMuY2xlYXJMaXN0ZW5lcigpfSx0LnByb3RvdHlwZS5fdXBkYXRlRHByPWZ1bmN0aW9uKCl7dmFyIGU7dGhpcy5fb3V0ZXJMaXN0ZW5lciYmKG51bGw9PT0oZT10aGlzLl9yZXNvbHV0aW9uTWVkaWFNYXRjaExpc3QpfHx2b2lkIDA9PT1lfHxlLnJlbW92ZUxpc3RlbmVyKHRoaXMuX291dGVyTGlzdGVuZXIpLHRoaXMuX2N1cnJlbnREZXZpY2VQaXhlbFJhdGlvPXdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHRoaXMuX3Jlc29sdXRpb25NZWRpYU1hdGNoTGlzdD13aW5kb3cubWF0Y2hNZWRpYSgic2NyZWVuIGFuZCAocmVzb2x1dGlvbjogIit3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbysiZHBweCkiKSx0aGlzLl9yZXNvbHV0aW9uTWVkaWFNYXRjaExpc3QuYWRkTGlzdGVuZXIodGhpcy5fb3V0ZXJMaXN0ZW5lcikpfSx0LnByb3RvdHlwZS5jbGVhckxpc3RlbmVyPWZ1bmN0aW9uKCl7dGhpcy5fcmVzb2x1dGlvbk1lZGlhTWF0Y2hMaXN0JiZ0aGlzLl9saXN0ZW5lciYmdGhpcy5fb3V0ZXJMaXN0ZW5lciYmKHRoaXMuX3Jlc29sdXRpb25NZWRpYU1hdGNoTGlzdC5yZW1vdmVMaXN0ZW5lcih0aGlzLl9vdXRlckxpc3RlbmVyKSx0aGlzLl9yZXNvbHV0aW9uTWVkaWFNYXRjaExpc3Q9dm9pZCAwLHRoaXMuX2xpc3RlbmVyPXZvaWQgMCx0aGlzLl9vdXRlckxpc3RlbmVyPXZvaWQgMCl9LHR9KHIoODQ0KS5EaXNwb3NhYmxlKTt0LlNjcmVlbkRwck1vbml0b3I9b30sMzIzNjpmdW5jdGlvbihlLHQscil7dmFyIGksbj10aGlzJiZ0aGlzLl9fZXh0ZW5kc3x8KGk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gaT1PYmplY3Quc2V0UHJvdG90eXBlT2Z8fHtfX3Byb3RvX186W119aW5zdGFuY2VvZiBBcnJheSYmZnVuY3Rpb24oZSx0KXtlLl9fcHJvdG9fXz10fXx8ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHIgaW4gdClPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodCxyKSYmKGVbcl09dFtyXSl9LGkoZSx0KX0sZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgdCYmbnVsbCE9PXQpdGhyb3cgbmV3IFR5cGVFcnJvcigiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSAiK1N0cmluZyh0KSsiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGwiKTtmdW5jdGlvbiByKCl7dGhpcy5jb25zdHJ1Y3Rvcj1lfWkoZSx0KSxlLnByb3RvdHlwZT1udWxsPT09dD9PYmplY3QuY3JlYXRlKHQpOihyLnByb3RvdHlwZT10LnByb3RvdHlwZSxuZXcgcil9KTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5UZXJtaW5hbD12b2lkIDA7dmFyIG89cigyOTUwKSxzPXIoMTY4MCksYT1yKDM2MTQpLGM9cigyNTg0KSxsPXIoNTQzNSksdT1yKDM1MjUpLGg9cigzNTUxKSxmPXIoOTMxMiksXz1yKDYxMTQpLGQ9cigzNjU2KSxwPXIoOTA0Miksdj1yKDM1NyksZz1yKDY5NTQpLHk9cig0NTY3KSxtPXIoMTI5NiksYj1yKDczOTkpLFM9cig4NDYwKSxDPXIoODQzNyksdz1yKDU2ODApLEw9cigzMjMwKSxFPXIoNDcyNSkseD1yKDQyOCksQT1yKDg5MzQpLGs9cig2NDY1KSxNPXIoNTExNCksUj1yKDg5NjkpLFQ9cig0Nzc0KSxPPXIoNDI2OSksQj1yKDU5NDEpLEQ9InVuZGVmaW5lZCIhPXR5cGVvZiB3aW5kb3c/d2luZG93LmRvY3VtZW50Om51bGwsUD1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQpe3ZvaWQgMD09PXQmJih0PXt9KTt2YXIgcj1lLmNhbGwodGhpcyx0KXx8dGhpcztyZXR1cm4gci5icm93c2VyPV8sci5fa2V5RG93bkhhbmRsZWQ9ITEsci5fa2V5UHJlc3NIYW5kbGVkPSExLHIuX3VucHJvY2Vzc2VkRGVhZEtleT0hMSxyLl9vbkN1cnNvck1vdmU9bmV3IFMuRXZlbnRFbWl0dGVyLHIuX29uS2V5PW5ldyBTLkV2ZW50RW1pdHRlcixyLl9vblJlbmRlcj1uZXcgUy5FdmVudEVtaXR0ZXIsci5fb25TZWxlY3Rpb25DaGFuZ2U9bmV3IFMuRXZlbnRFbWl0dGVyLHIuX29uVGl0bGVDaGFuZ2U9bmV3IFMuRXZlbnRFbWl0dGVyLHIuX29uQmVsbD1uZXcgUy5FdmVudEVtaXR0ZXIsci5fb25Gb2N1cz1uZXcgUy5FdmVudEVtaXR0ZXIsci5fb25CbHVyPW5ldyBTLkV2ZW50RW1pdHRlcixyLl9vbkExMXlDaGFyRW1pdHRlcj1uZXcgUy5FdmVudEVtaXR0ZXIsci5fb25BMTF5VGFiRW1pdHRlcj1uZXcgUy5FdmVudEVtaXR0ZXIsci5fc2V0dXAoKSxyLmxpbmtpZmllcj1yLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShoLkxpbmtpZmllciksci5saW5raWZpZXIyPXIucmVnaXN0ZXIoci5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2Uoay5MaW5raWZpZXIyKSksci5yZWdpc3RlcihyLl9pbnB1dEhhbmRsZXIub25SZXF1ZXN0QmVsbCgoZnVuY3Rpb24oKXtyZXR1cm4gci5iZWxsKCl9KSkpLHIucmVnaXN0ZXIoci5faW5wdXRIYW5kbGVyLm9uUmVxdWVzdFJlZnJlc2hSb3dzKChmdW5jdGlvbihlLHQpe3JldHVybiByLnJlZnJlc2goZSx0KX0pKSksci5yZWdpc3RlcihyLl9pbnB1dEhhbmRsZXIub25SZXF1ZXN0U2VuZEZvY3VzKChmdW5jdGlvbigpe3JldHVybiByLl9yZXBvcnRGb2N1cygpfSkpKSxyLnJlZ2lzdGVyKHIuX2lucHV0SGFuZGxlci5vblJlcXVlc3RSZXNldCgoZnVuY3Rpb24oKXtyZXR1cm4gci5yZXNldCgpfSkpKSxyLnJlZ2lzdGVyKHIuX2lucHV0SGFuZGxlci5vblJlcXVlc3RXaW5kb3dzT3B0aW9uc1JlcG9ydCgoZnVuY3Rpb24oZSl7cmV0dXJuIHIuX3JlcG9ydFdpbmRvd3NPcHRpb25zKGUpfSkpKSxyLnJlZ2lzdGVyKHIuX2lucHV0SGFuZGxlci5vbkNvbG9yKChmdW5jdGlvbihlKXtyZXR1cm4gci5faGFuZGxlQ29sb3JFdmVudChlKX0pKSksci5yZWdpc3RlcigoMCxTLmZvcndhcmRFdmVudCkoci5faW5wdXRIYW5kbGVyLm9uQ3Vyc29yTW92ZSxyLl9vbkN1cnNvck1vdmUpKSxyLnJlZ2lzdGVyKCgwLFMuZm9yd2FyZEV2ZW50KShyLl9pbnB1dEhhbmRsZXIub25UaXRsZUNoYW5nZSxyLl9vblRpdGxlQ2hhbmdlKSksci5yZWdpc3RlcigoMCxTLmZvcndhcmRFdmVudCkoci5faW5wdXRIYW5kbGVyLm9uQTExeUNoYXIsci5fb25BMTF5Q2hhckVtaXR0ZXIpKSxyLnJlZ2lzdGVyKCgwLFMuZm9yd2FyZEV2ZW50KShyLl9pbnB1dEhhbmRsZXIub25BMTF5VGFiLHIuX29uQTExeVRhYkVtaXR0ZXIpKSxyLnJlZ2lzdGVyKHIuX2J1ZmZlclNlcnZpY2Uub25SZXNpemUoKGZ1bmN0aW9uKGUpe3JldHVybiByLl9hZnRlclJlc2l6ZShlLmNvbHMsZS5yb3dzKX0pKSkscn1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25DdXJzb3JNb3ZlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uQ3Vyc29yTW92ZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uS2V5Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uS2V5LmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZW5kZXIiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZW5kZXIuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblNlbGVjdGlvbkNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblNlbGVjdGlvbkNoYW5nZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uVGl0bGVDaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25UaXRsZUNoYW5nZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQmVsbCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkJlbGwuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkZvY3VzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uRm9jdXMuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkJsdXIiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25CbHVyLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25BMTF5Q2hhciIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkExMXlDaGFyRW1pdHRlci5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQTExeVRhYiIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkExMXlUYWJFbWl0dGVyLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLHQucHJvdG90eXBlLl9oYW5kbGVDb2xvckV2ZW50PWZ1bmN0aW9uKGUpe3ZhciB0LHI7aWYodGhpcy5fY29sb3JNYW5hZ2VyKXtmb3IodmFyIGk9MCxuPWU7aTxuLmxlbmd0aDtpKyspe3ZhciBvPW5baV0scz12b2lkIDAsYT0iIjtzd2l0Y2goby5pbmRleCl7Y2FzZSAyNTY6cz0iZm9yZWdyb3VuZCIsYT0iMTAiO2JyZWFrO2Nhc2UgMjU3OnM9ImJhY2tncm91bmQiLGE9IjExIjticmVhaztjYXNlIDI1ODpzPSJjdXJzb3IiLGE9IjEyIjticmVhaztkZWZhdWx0OnM9ImFuc2kiLGE9IjQ7IitvLmluZGV4fWlmKHMpc3dpdGNoKG8udHlwZSl7Y2FzZSAwOnZhciBsPVQuY29sb3IudG9Db2xvclJHQigiYW5zaSI9PT1zP3RoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMuYW5zaVtvLmluZGV4XTp0aGlzLl9jb2xvck1hbmFnZXIuY29sb3JzW3NdKTt0aGlzLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoYy5DMC5FU0MrIl0iK2ErIjsiKygwLEIudG9SZ2JTdHJpbmcpKGwpK2MuQzAuQkVMKTticmVhaztjYXNlIDE6ImFuc2kiPT09cz90aGlzLl9jb2xvck1hbmFnZXIuY29sb3JzLmFuc2lbby5pbmRleF09VC5yZ2JhLnRvQ29sb3IuYXBwbHkoVC5yZ2JhLG8uY29sb3IpOnRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnNbc109VC5yZ2JhLnRvQ29sb3IuYXBwbHkoVC5yZ2JhLG8uY29sb3IpO2JyZWFrO2Nhc2UgMjp0aGlzLl9jb2xvck1hbmFnZXIucmVzdG9yZUNvbG9yKG8uaW5kZXgpfX1udWxsPT09KHQ9dGhpcy5fcmVuZGVyU2VydmljZSl8fHZvaWQgMD09PXR8fHQuc2V0Q29sb3JzKHRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMpLG51bGw9PT0ocj10aGlzLnZpZXdwb3J0KXx8dm9pZCAwPT09cnx8ci5vblRoZW1lQ2hhbmdlKHRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMpfX0sdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3ZhciB0LHIsaTt0aGlzLl9pc0Rpc3Bvc2VkfHwoZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpLG51bGw9PT0odD10aGlzLl9yZW5kZXJTZXJ2aWNlKXx8dm9pZCAwPT09dHx8dC5kaXNwb3NlKCksdGhpcy5fY3VzdG9tS2V5RXZlbnRIYW5kbGVyPXZvaWQgMCx0aGlzLndyaXRlPWZ1bmN0aW9uKCl7fSxudWxsPT09KGk9bnVsbD09PShyPXRoaXMuZWxlbWVudCl8fHZvaWQgMD09PXI/dm9pZCAwOnIucGFyZW50Tm9kZSl8fHZvaWQgMD09PWl8fGkucmVtb3ZlQ2hpbGQodGhpcy5lbGVtZW50KSl9LHQucHJvdG90eXBlLl9zZXR1cD1mdW5jdGlvbigpe2UucHJvdG90eXBlLl9zZXR1cC5jYWxsKHRoaXMpLHRoaXMuX2N1c3RvbUtleUV2ZW50SGFuZGxlcj12b2lkIDB9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwiYnVmZmVyIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuYnVmZmVycy5hY3RpdmV9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZm9jdXM9ZnVuY3Rpb24oKXt0aGlzLnRleHRhcmVhJiZ0aGlzLnRleHRhcmVhLmZvY3VzKHtwcmV2ZW50U2Nyb2xsOiEwfSl9LHQucHJvdG90eXBlLl91cGRhdGVPcHRpb25zPWZ1bmN0aW9uKHQpe3ZhciByLGksbixvO3N3aXRjaChlLnByb3RvdHlwZS5fdXBkYXRlT3B0aW9ucy5jYWxsKHRoaXMsdCksdCl7Y2FzZSJmb250RmFtaWx5IjpjYXNlImZvbnRTaXplIjpudWxsPT09KHI9dGhpcy5fcmVuZGVyU2VydmljZSl8fHZvaWQgMD09PXJ8fHIuY2xlYXIoKSxudWxsPT09KGk9dGhpcy5fY2hhclNpemVTZXJ2aWNlKXx8dm9pZCAwPT09aXx8aS5tZWFzdXJlKCk7YnJlYWs7Y2FzZSJjdXJzb3JCbGluayI6Y2FzZSJjdXJzb3JTdHlsZSI6dGhpcy5yZWZyZXNoKHRoaXMuYnVmZmVyLnksdGhpcy5idWZmZXIueSk7YnJlYWs7Y2FzZSJjdXN0b21HbHlwaHMiOmNhc2UiZHJhd0JvbGRUZXh0SW5CcmlnaHRDb2xvcnMiOmNhc2UibGV0dGVyU3BhY2luZyI6Y2FzZSJsaW5lSGVpZ2h0IjpjYXNlImZvbnRXZWlnaHQiOmNhc2UiZm9udFdlaWdodEJvbGQiOmNhc2UibWluaW11bUNvbnRyYXN0UmF0aW8iOnRoaXMuX3JlbmRlclNlcnZpY2UmJih0aGlzLl9yZW5kZXJTZXJ2aWNlLmNsZWFyKCksdGhpcy5fcmVuZGVyU2VydmljZS5vblJlc2l6ZSh0aGlzLmNvbHMsdGhpcy5yb3dzKSx0aGlzLnJlZnJlc2goMCx0aGlzLnJvd3MtMSkpO2JyZWFrO2Nhc2UicmVuZGVyZXJUeXBlIjp0aGlzLl9yZW5kZXJTZXJ2aWNlJiYodGhpcy5fcmVuZGVyU2VydmljZS5zZXRSZW5kZXJlcih0aGlzLl9jcmVhdGVSZW5kZXJlcigpKSx0aGlzLl9yZW5kZXJTZXJ2aWNlLm9uUmVzaXplKHRoaXMuY29scyx0aGlzLnJvd3MpKTticmVhaztjYXNlInNjcm9sbGJhY2siOm51bGw9PT0obj10aGlzLnZpZXdwb3J0KXx8dm9pZCAwPT09bnx8bi5zeW5jU2Nyb2xsQXJlYSgpO2JyZWFrO2Nhc2Uic2NyZWVuUmVhZGVyTW9kZSI6dGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcmVlblJlYWRlck1vZGU/IXRoaXMuX2FjY2Vzc2liaWxpdHlNYW5hZ2VyJiZ0aGlzLl9yZW5kZXJTZXJ2aWNlJiYodGhpcy5fYWNjZXNzaWJpbGl0eU1hbmFnZXI9bmV3IHkuQWNjZXNzaWJpbGl0eU1hbmFnZXIodGhpcyx0aGlzLl9yZW5kZXJTZXJ2aWNlKSk6KG51bGw9PT0obz10aGlzLl9hY2Nlc3NpYmlsaXR5TWFuYWdlcil8fHZvaWQgMD09PW98fG8uZGlzcG9zZSgpLHRoaXMuX2FjY2Vzc2liaWxpdHlNYW5hZ2VyPXZvaWQgMCk7YnJlYWs7Y2FzZSJ0YWJTdG9wV2lkdGgiOnRoaXMuYnVmZmVycy5zZXR1cFRhYlN0b3BzKCk7YnJlYWs7Y2FzZSJ0aGVtZSI6dGhpcy5fc2V0VGhlbWUodGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zLnRoZW1lKX19LHQucHJvdG90eXBlLl9vblRleHRBcmVhRm9jdXM9ZnVuY3Rpb24oZSl7dGhpcy5jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuc2VuZEZvY3VzJiZ0aGlzLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoYy5DMC5FU0MrIltJIiksdGhpcy51cGRhdGVDdXJzb3JTdHlsZShlKSx0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LmFkZCgiZm9jdXMiKSx0aGlzLl9zaG93Q3Vyc29yKCksdGhpcy5fb25Gb2N1cy5maXJlKCl9LHQucHJvdG90eXBlLmJsdXI9ZnVuY3Rpb24oKXt2YXIgZTtyZXR1cm4gbnVsbD09PShlPXRoaXMudGV4dGFyZWEpfHx2b2lkIDA9PT1lP3ZvaWQgMDplLmJsdXIoKX0sdC5wcm90b3R5cGUuX29uVGV4dEFyZWFCbHVyPWZ1bmN0aW9uKCl7dGhpcy50ZXh0YXJlYS52YWx1ZT0iIix0aGlzLnJlZnJlc2godGhpcy5idWZmZXIueSx0aGlzLmJ1ZmZlci55KSx0aGlzLmNvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5zZW5kRm9jdXMmJnRoaXMuY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChjLkMwLkVTQysiW08iKSx0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgiZm9jdXMiKSx0aGlzLl9vbkJsdXIuZmlyZSgpfSx0LnByb3RvdHlwZS5fc3luY1RleHRBcmVhPWZ1bmN0aW9uKCl7aWYodGhpcy50ZXh0YXJlYSYmdGhpcy5idWZmZXIuaXNDdXJzb3JJblZpZXdwb3J0JiYhdGhpcy5fY29tcG9zaXRpb25IZWxwZXIuaXNDb21wb3NpbmcmJnRoaXMuX3JlbmRlclNlcnZpY2Upe3ZhciBlPXRoaXMuYnVmZmVyLnliYXNlK3RoaXMuYnVmZmVyLnksdD10aGlzLmJ1ZmZlci5saW5lcy5nZXQoZSk7aWYodCl7dmFyIHI9TWF0aC5taW4odGhpcy5idWZmZXIueCx0aGlzLmNvbHMtMSksaT10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbEhlaWdodCxuPXQuZ2V0V2lkdGgociksbz10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoKm4scz10aGlzLmJ1ZmZlci55KnRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0LGE9cip0aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoO3RoaXMudGV4dGFyZWEuc3R5bGUubGVmdD1hKyJweCIsdGhpcy50ZXh0YXJlYS5zdHlsZS50b3A9cysicHgiLHRoaXMudGV4dGFyZWEuc3R5bGUud2lkdGg9bysicHgiLHRoaXMudGV4dGFyZWEuc3R5bGUuaGVpZ2h0PWkrInB4Iix0aGlzLnRleHRhcmVhLnN0eWxlLmxpbmVIZWlnaHQ9aSsicHgiLHRoaXMudGV4dGFyZWEuc3R5bGUuekluZGV4PSItNSJ9fX0sdC5wcm90b3R5cGUuX2luaXRHbG9iYWw9ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3RoaXMuX2JpbmRLZXlzKCksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy5lbGVtZW50LCJjb3B5IiwoZnVuY3Rpb24odCl7ZS5oYXNTZWxlY3Rpb24oKSYmKDAsYS5jb3B5SGFuZGxlcikodCxlLl9zZWxlY3Rpb25TZXJ2aWNlKX0pKSk7dmFyIHQ9ZnVuY3Rpb24odCl7cmV0dXJuKDAsYS5oYW5kbGVQYXN0ZUV2ZW50KSh0LGUudGV4dGFyZWEsZS5jb3JlU2VydmljZSl9O3RoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsInBhc3RlIix0KSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy5lbGVtZW50LCJwYXN0ZSIsdCkpLF8uaXNGaXJlZm94P3RoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMuZWxlbWVudCwibW91c2Vkb3duIiwoZnVuY3Rpb24odCl7Mj09PXQuYnV0dG9uJiYoMCxhLnJpZ2h0Q2xpY2tIYW5kbGVyKSh0LGUudGV4dGFyZWEsZS5zY3JlZW5FbGVtZW50LGUuX3NlbGVjdGlvblNlcnZpY2UsZS5vcHRpb25zLnJpZ2h0Q2xpY2tTZWxlY3RzV29yZCl9KSkpOnRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMuZWxlbWVudCwiY29udGV4dG1lbnUiLChmdW5jdGlvbih0KXsoMCxhLnJpZ2h0Q2xpY2tIYW5kbGVyKSh0LGUudGV4dGFyZWEsZS5zY3JlZW5FbGVtZW50LGUuX3NlbGVjdGlvblNlcnZpY2UsZS5vcHRpb25zLnJpZ2h0Q2xpY2tTZWxlY3RzV29yZCl9KSkpLF8uaXNMaW51eCYmdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy5lbGVtZW50LCJhdXhjbGljayIsKGZ1bmN0aW9uKHQpezE9PT10LmJ1dHRvbiYmKDAsYS5tb3ZlVGV4dEFyZWFVbmRlck1vdXNlQ3Vyc29yKSh0LGUudGV4dGFyZWEsZS5zY3JlZW5FbGVtZW50KX0pKSl9LHQucHJvdG90eXBlLl9iaW5kS2V5cz1mdW5jdGlvbigpe3ZhciBlPXRoaXM7dGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy50ZXh0YXJlYSwia2V5dXAiLChmdW5jdGlvbih0KXtyZXR1cm4gZS5fa2V5VXAodCl9KSwhMCkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImtleWRvd24iLChmdW5jdGlvbih0KXtyZXR1cm4gZS5fa2V5RG93bih0KX0pLCEwKSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy50ZXh0YXJlYSwia2V5cHJlc3MiLChmdW5jdGlvbih0KXtyZXR1cm4gZS5fa2V5UHJlc3ModCl9KSwhMCkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImNvbXBvc2l0aW9uc3RhcnQiLChmdW5jdGlvbigpe3JldHVybiBlLl9jb21wb3NpdGlvbkhlbHBlci5jb21wb3NpdGlvbnN0YXJ0KCl9KSkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImNvbXBvc2l0aW9udXBkYXRlIiwoZnVuY3Rpb24odCl7cmV0dXJuIGUuX2NvbXBvc2l0aW9uSGVscGVyLmNvbXBvc2l0aW9udXBkYXRlKHQpfSkpKSx0aGlzLnJlZ2lzdGVyKCgwLGQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLnRleHRhcmVhLCJjb21wb3NpdGlvbmVuZCIsKGZ1bmN0aW9uKCl7cmV0dXJuIGUuX2NvbXBvc2l0aW9uSGVscGVyLmNvbXBvc2l0aW9uZW5kKCl9KSkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImlucHV0IiwoZnVuY3Rpb24odCl7cmV0dXJuIGUuX2lucHV0RXZlbnQodCl9KSwhMCkpLHRoaXMucmVnaXN0ZXIodGhpcy5vblJlbmRlcigoZnVuY3Rpb24oKXtyZXR1cm4gZS5fY29tcG9zaXRpb25IZWxwZXIudXBkYXRlQ29tcG9zaXRpb25FbGVtZW50cygpfSkpKSx0aGlzLnJlZ2lzdGVyKHRoaXMub25SZW5kZXIoKGZ1bmN0aW9uKHQpe3JldHVybiBlLl9xdWV1ZUxpbmtpZmljYXRpb24odC5zdGFydCx0LmVuZCl9KSkpfSx0LnByb3RvdHlwZS5vcGVuPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7aWYoIWUpdGhyb3cgbmV3IEVycm9yKCJUZXJtaW5hbCByZXF1aXJlcyBhIHBhcmVudCBlbGVtZW50LiIpO2UuaXNDb25uZWN0ZWR8fHRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIlRlcm1pbmFsLm9wZW4gd2FzIGNhbGxlZCBvbiBhbiBlbGVtZW50IHRoYXQgd2FzIG5vdCBhdHRhY2hlZCB0byB0aGUgRE9NIiksdGhpcy5fZG9jdW1lbnQ9ZS5vd25lckRvY3VtZW50LHRoaXMuZWxlbWVudD10aGlzLl9kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKSx0aGlzLmVsZW1lbnQuZGlyPSJsdHIiLHRoaXMuZWxlbWVudC5jbGFzc0xpc3QuYWRkKCJ0ZXJtaW5hbCIpLHRoaXMuZWxlbWVudC5jbGFzc0xpc3QuYWRkKCJ4dGVybSIpLHRoaXMuZWxlbWVudC5zZXRBdHRyaWJ1dGUoInRhYmluZGV4IiwiMCIpLGUuYXBwZW5kQ2hpbGQodGhpcy5lbGVtZW50KTt2YXIgcj1ELmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTt0aGlzLl92aWV3cG9ydEVsZW1lbnQ9RC5jcmVhdGVFbGVtZW50KCJkaXYiKSx0aGlzLl92aWV3cG9ydEVsZW1lbnQuY2xhc3NMaXN0LmFkZCgieHRlcm0tdmlld3BvcnQiKSxyLmFwcGVuZENoaWxkKHRoaXMuX3ZpZXdwb3J0RWxlbWVudCksdGhpcy5fdmlld3BvcnRTY3JvbGxBcmVhPUQuY3JlYXRlRWxlbWVudCgiZGl2IiksdGhpcy5fdmlld3BvcnRTY3JvbGxBcmVhLmNsYXNzTGlzdC5hZGQoInh0ZXJtLXNjcm9sbC1hcmVhIiksdGhpcy5fdmlld3BvcnRFbGVtZW50LmFwcGVuZENoaWxkKHRoaXMuX3ZpZXdwb3J0U2Nyb2xsQXJlYSksdGhpcy5zY3JlZW5FbGVtZW50PUQuY3JlYXRlRWxlbWVudCgiZGl2IiksdGhpcy5zY3JlZW5FbGVtZW50LmNsYXNzTGlzdC5hZGQoInh0ZXJtLXNjcmVlbiIpLHRoaXMuX2hlbHBlckNvbnRhaW5lcj1ELmNyZWF0ZUVsZW1lbnQoImRpdiIpLHRoaXMuX2hlbHBlckNvbnRhaW5lci5jbGFzc0xpc3QuYWRkKCJ4dGVybS1oZWxwZXJzIiksdGhpcy5zY3JlZW5FbGVtZW50LmFwcGVuZENoaWxkKHRoaXMuX2hlbHBlckNvbnRhaW5lciksci5hcHBlbmRDaGlsZCh0aGlzLnNjcmVlbkVsZW1lbnQpLHRoaXMudGV4dGFyZWE9RC5jcmVhdGVFbGVtZW50KCJ0ZXh0YXJlYSIpLHRoaXMudGV4dGFyZWEuY2xhc3NMaXN0LmFkZCgieHRlcm0taGVscGVyLXRleHRhcmVhIiksdGhpcy50ZXh0YXJlYS5zZXRBdHRyaWJ1dGUoImFyaWEtbGFiZWwiLHAucHJvbXB0TGFiZWwpLHRoaXMudGV4dGFyZWEuc2V0QXR0cmlidXRlKCJhcmlhLW11bHRpbGluZSIsImZhbHNlIiksdGhpcy50ZXh0YXJlYS5zZXRBdHRyaWJ1dGUoImF1dG9jb3JyZWN0Iiwib2ZmIiksdGhpcy50ZXh0YXJlYS5zZXRBdHRyaWJ1dGUoImF1dG9jYXBpdGFsaXplIiwib2ZmIiksdGhpcy50ZXh0YXJlYS5zZXRBdHRyaWJ1dGUoInNwZWxsY2hlY2siLCJmYWxzZSIpLHRoaXMudGV4dGFyZWEudGFiSW5kZXg9MCx0aGlzLnJlZ2lzdGVyKCgwLGQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLnRleHRhcmVhLCJmb2N1cyIsKGZ1bmN0aW9uKGUpe3JldHVybiB0Ll9vblRleHRBcmVhRm9jdXMoZSl9KSkpLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHRoaXMudGV4dGFyZWEsImJsdXIiLChmdW5jdGlvbigpe3JldHVybiB0Ll9vblRleHRBcmVhQmx1cigpfSkpKSx0aGlzLl9oZWxwZXJDb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy50ZXh0YXJlYSk7dmFyIGk9dGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2UoTS5Db3JlQnJvd3NlclNlcnZpY2UsdGhpcy50ZXh0YXJlYSk7dGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklDb3JlQnJvd3NlclNlcnZpY2UsaSksdGhpcy5fY2hhclNpemVTZXJ2aWNlPXRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKHguQ2hhclNpemVTZXJ2aWNlLHRoaXMuX2RvY3VtZW50LHRoaXMuX2hlbHBlckNvbnRhaW5lciksdGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklDaGFyU2l6ZVNlcnZpY2UsdGhpcy5fY2hhclNpemVTZXJ2aWNlKSx0aGlzLl90aGVtZT10aGlzLm9wdGlvbnMudGhlbWV8fHRoaXMuX3RoZW1lLHRoaXMuX2NvbG9yTWFuYWdlcj1uZXcgdy5Db2xvck1hbmFnZXIoRCx0aGlzLm9wdGlvbnMuYWxsb3dUcmFuc3BhcmVuY3kpLHRoaXMucmVnaXN0ZXIodGhpcy5vcHRpb25zU2VydmljZS5vbk9wdGlvbkNoYW5nZSgoZnVuY3Rpb24oZSl7cmV0dXJuIHQuX2NvbG9yTWFuYWdlci5vbk9wdGlvbnNDaGFuZ2UoZSl9KSkpLHRoaXMuX2NvbG9yTWFuYWdlci5zZXRUaGVtZSh0aGlzLl90aGVtZSksdGhpcy5fY2hhcmFjdGVySm9pbmVyU2VydmljZT10aGlzLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShPLkNoYXJhY3RlckpvaW5lclNlcnZpY2UpLHRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2UoRS5JQ2hhcmFjdGVySm9pbmVyU2VydmljZSx0aGlzLl9jaGFyYWN0ZXJKb2luZXJTZXJ2aWNlKTt2YXIgbj10aGlzLl9jcmVhdGVSZW5kZXJlcigpO3RoaXMuX3JlbmRlclNlcnZpY2U9dGhpcy5yZWdpc3Rlcih0aGlzLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShMLlJlbmRlclNlcnZpY2Usbix0aGlzLnJvd3MsdGhpcy5zY3JlZW5FbGVtZW50KSksdGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklSZW5kZXJTZXJ2aWNlLHRoaXMuX3JlbmRlclNlcnZpY2UpLHRoaXMucmVnaXN0ZXIodGhpcy5fcmVuZGVyU2VydmljZS5vblJlbmRlcmVkQnVmZmVyQ2hhbmdlKChmdW5jdGlvbihlKXtyZXR1cm4gdC5fb25SZW5kZXIuZmlyZShlKX0pKSksdGhpcy5vblJlc2l6ZSgoZnVuY3Rpb24oZSl7cmV0dXJuIHQuX3JlbmRlclNlcnZpY2UucmVzaXplKGUuY29scyxlLnJvd3MpfSkpLHRoaXMuX2NvbXBvc2l0aW9uVmlldz1ELmNyZWF0ZUVsZW1lbnQoImRpdiIpLHRoaXMuX2NvbXBvc2l0aW9uVmlldy5jbGFzc0xpc3QuYWRkKCJjb21wb3NpdGlvbi12aWV3IiksdGhpcy5fY29tcG9zaXRpb25IZWxwZXI9dGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2Uoby5Db21wb3NpdGlvbkhlbHBlcix0aGlzLnRleHRhcmVhLHRoaXMuX2NvbXBvc2l0aW9uVmlldyksdGhpcy5faGVscGVyQ29udGFpbmVyLmFwcGVuZENoaWxkKHRoaXMuX2NvbXBvc2l0aW9uVmlldyksdGhpcy5lbGVtZW50LmFwcGVuZENoaWxkKHIpLHRoaXMuX3NvdW5kU2VydmljZT10aGlzLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZSh2LlNvdW5kU2VydmljZSksdGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklTb3VuZFNlcnZpY2UsdGhpcy5fc291bmRTZXJ2aWNlKSx0aGlzLl9tb3VzZVNlcnZpY2U9dGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2UoQS5Nb3VzZVNlcnZpY2UpLHRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2UoRS5JTW91c2VTZXJ2aWNlLHRoaXMuX21vdXNlU2VydmljZSksdGhpcy52aWV3cG9ydD10aGlzLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShzLlZpZXdwb3J0LChmdW5jdGlvbihlKXtyZXR1cm4gdC5zY3JvbGxMaW5lcyhlLCEwLDEpfSksdGhpcy5fdmlld3BvcnRFbGVtZW50LHRoaXMuX3ZpZXdwb3J0U2Nyb2xsQXJlYSx0aGlzLmVsZW1lbnQpLHRoaXMudmlld3BvcnQub25UaGVtZUNoYW5nZSh0aGlzLl9jb2xvck1hbmFnZXIuY29sb3JzKSx0aGlzLnJlZ2lzdGVyKHRoaXMuX2lucHV0SGFuZGxlci5vblJlcXVlc3RTeW5jU2Nyb2xsQmFyKChmdW5jdGlvbigpe3JldHVybiB0LnZpZXdwb3J0LnN5bmNTY3JvbGxBcmVhKCl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy52aWV3cG9ydCksdGhpcy5yZWdpc3Rlcih0aGlzLm9uQ3Vyc29yTW92ZSgoZnVuY3Rpb24oKXt0Ll9yZW5kZXJTZXJ2aWNlLm9uQ3Vyc29yTW92ZSgpLHQuX3N5bmNUZXh0QXJlYSgpfSkpKSx0aGlzLnJlZ2lzdGVyKHRoaXMub25SZXNpemUoKGZ1bmN0aW9uKCl7cmV0dXJuIHQuX3JlbmRlclNlcnZpY2Uub25SZXNpemUodC5jb2xzLHQucm93cyl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy5vbkJsdXIoKGZ1bmN0aW9uKCl7cmV0dXJuIHQuX3JlbmRlclNlcnZpY2Uub25CbHVyKCl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy5vbkZvY3VzKChmdW5jdGlvbigpe3JldHVybiB0Ll9yZW5kZXJTZXJ2aWNlLm9uRm9jdXMoKX0pKSksdGhpcy5yZWdpc3Rlcih0aGlzLl9yZW5kZXJTZXJ2aWNlLm9uRGltZW5zaW9uc0NoYW5nZSgoZnVuY3Rpb24oKXtyZXR1cm4gdC52aWV3cG9ydC5zeW5jU2Nyb2xsQXJlYSgpfSkpKSx0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlPXRoaXMucmVnaXN0ZXIodGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2UoZi5TZWxlY3Rpb25TZXJ2aWNlLHRoaXMuZWxlbWVudCx0aGlzLnNjcmVlbkVsZW1lbnQsdGhpcy5saW5raWZpZXIyKSksdGhpcy5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShFLklTZWxlY3Rpb25TZXJ2aWNlLHRoaXMuX3NlbGVjdGlvblNlcnZpY2UpLHRoaXMucmVnaXN0ZXIodGhpcy5fc2VsZWN0aW9uU2VydmljZS5vblJlcXVlc3RTY3JvbGxMaW5lcygoZnVuY3Rpb24oZSl7cmV0dXJuIHQuc2Nyb2xsTGluZXMoZS5hbW91bnQsZS5zdXBwcmVzc1Njcm9sbEV2ZW50KX0pKSksdGhpcy5yZWdpc3Rlcih0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLm9uU2VsZWN0aW9uQ2hhbmdlKChmdW5jdGlvbigpe3JldHVybiB0Ll9vblNlbGVjdGlvbkNoYW5nZS5maXJlKCl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy5fc2VsZWN0aW9uU2VydmljZS5vblJlcXVlc3RSZWRyYXcoKGZ1bmN0aW9uKGUpe3JldHVybiB0Ll9yZW5kZXJTZXJ2aWNlLm9uU2VsZWN0aW9uQ2hhbmdlZChlLnN0YXJ0LGUuZW5kLGUuY29sdW1uU2VsZWN0TW9kZSl9KSkpLHRoaXMucmVnaXN0ZXIodGhpcy5fc2VsZWN0aW9uU2VydmljZS5vbkxpbnV4TW91c2VTZWxlY3Rpb24oKGZ1bmN0aW9uKGUpe3QudGV4dGFyZWEudmFsdWU9ZSx0LnRleHRhcmVhLmZvY3VzKCksdC50ZXh0YXJlYS5zZWxlY3QoKX0pKSksdGhpcy5yZWdpc3Rlcih0aGlzLl9vblNjcm9sbC5ldmVudCgoZnVuY3Rpb24oZSl7dC52aWV3cG9ydC5zeW5jU2Nyb2xsQXJlYSgpLHQuX3NlbGVjdGlvblNlcnZpY2UucmVmcmVzaCgpfSkpKSx0aGlzLnJlZ2lzdGVyKCgwLGQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKSh0aGlzLl92aWV3cG9ydEVsZW1lbnQsInNjcm9sbCIsKGZ1bmN0aW9uKCl7cmV0dXJuIHQuX3NlbGVjdGlvblNlcnZpY2UucmVmcmVzaCgpfSkpKSx0aGlzLl9tb3VzZVpvbmVNYW5hZ2VyPXRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKGcuTW91c2Vab25lTWFuYWdlcix0aGlzLmVsZW1lbnQsdGhpcy5zY3JlZW5FbGVtZW50KSx0aGlzLnJlZ2lzdGVyKHRoaXMuX21vdXNlWm9uZU1hbmFnZXIpLHRoaXMucmVnaXN0ZXIodGhpcy5vblNjcm9sbCgoZnVuY3Rpb24oKXtyZXR1cm4gdC5fbW91c2Vab25lTWFuYWdlci5jbGVhckFsbCgpfSkpKSx0aGlzLmxpbmtpZmllci5hdHRhY2hUb0RvbSh0aGlzLmVsZW1lbnQsdGhpcy5fbW91c2Vab25lTWFuYWdlciksdGhpcy5saW5raWZpZXIyLmF0dGFjaFRvRG9tKHRoaXMuc2NyZWVuRWxlbWVudCx0aGlzLl9tb3VzZVNlcnZpY2UsdGhpcy5fcmVuZGVyU2VydmljZSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikodGhpcy5lbGVtZW50LCJtb3VzZWRvd24iLChmdW5jdGlvbihlKXtyZXR1cm4gdC5fc2VsZWN0aW9uU2VydmljZS5vbk1vdXNlRG93bihlKX0pKSksdGhpcy5jb3JlTW91c2VTZXJ2aWNlLmFyZU1vdXNlRXZlbnRzQWN0aXZlPyh0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLmRpc2FibGUoKSx0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LmFkZCgiZW5hYmxlLW1vdXNlLWV2ZW50cyIpKTp0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLmVuYWJsZSgpLHRoaXMub3B0aW9ucy5zY3JlZW5SZWFkZXJNb2RlJiYodGhpcy5fYWNjZXNzaWJpbGl0eU1hbmFnZXI9bmV3IHkuQWNjZXNzaWJpbGl0eU1hbmFnZXIodGhpcyx0aGlzLl9yZW5kZXJTZXJ2aWNlKSksdGhpcy5fY2hhclNpemVTZXJ2aWNlLm1lYXN1cmUoKSx0aGlzLnJlZnJlc2goMCx0aGlzLnJvd3MtMSksdGhpcy5faW5pdEdsb2JhbCgpLHRoaXMuYmluZE1vdXNlKCl9LHQucHJvdG90eXBlLl9jcmVhdGVSZW5kZXJlcj1mdW5jdGlvbigpe3N3aXRjaCh0aGlzLm9wdGlvbnMucmVuZGVyZXJUeXBlKXtjYXNlImNhbnZhcyI6cmV0dXJuIHRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKHUuUmVuZGVyZXIsdGhpcy5fY29sb3JNYW5hZ2VyLmNvbG9ycyx0aGlzLnNjcmVlbkVsZW1lbnQsdGhpcy5saW5raWZpZXIsdGhpcy5saW5raWZpZXIyKTtjYXNlImRvbSI6cmV0dXJuIHRoaXMuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKG0uRG9tUmVuZGVyZXIsdGhpcy5fY29sb3JNYW5hZ2VyLmNvbG9ycyx0aGlzLmVsZW1lbnQsdGhpcy5zY3JlZW5FbGVtZW50LHRoaXMuX3ZpZXdwb3J0RWxlbWVudCx0aGlzLmxpbmtpZmllcix0aGlzLmxpbmtpZmllcjIpO2RlZmF1bHQ6dGhyb3cgbmV3IEVycm9yKCdVbnJlY29nbml6ZWQgcmVuZGVyZXJUeXBlICInK3RoaXMub3B0aW9ucy5yZW5kZXJlclR5cGUrJyInKX19LHQucHJvdG90eXBlLl9zZXRUaGVtZT1mdW5jdGlvbihlKXt2YXIgdCxyLGk7dGhpcy5fdGhlbWU9ZSxudWxsPT09KHQ9dGhpcy5fY29sb3JNYW5hZ2VyKXx8dm9pZCAwPT09dHx8dC5zZXRUaGVtZShlKSxudWxsPT09KHI9dGhpcy5fcmVuZGVyU2VydmljZSl8fHZvaWQgMD09PXJ8fHIuc2V0Q29sb3JzKHRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMpLG51bGw9PT0oaT10aGlzLnZpZXdwb3J0KXx8dm9pZCAwPT09aXx8aS5vblRoZW1lQ2hhbmdlKHRoaXMuX2NvbG9yTWFuYWdlci5jb2xvcnMpfSx0LnByb3RvdHlwZS5iaW5kTW91c2U9ZnVuY3Rpb24oKXt2YXIgZT10aGlzLHQ9dGhpcyxyPXRoaXMuZWxlbWVudDtmdW5jdGlvbiBpKGUpe3ZhciByLGksbj10Ll9tb3VzZVNlcnZpY2UuZ2V0UmF3Qnl0ZUNvb3JkcyhlLHQuc2NyZWVuRWxlbWVudCx0LmNvbHMsdC5yb3dzKTtpZighbilyZXR1cm4hMTtzd2l0Y2goZS5vdmVycmlkZVR5cGV8fGUudHlwZSl7Y2FzZSJtb3VzZW1vdmUiOmk9MzIsdm9pZCAwPT09ZS5idXR0b25zPyhyPTMsdm9pZCAwIT09ZS5idXR0b24mJihyPWUuYnV0dG9uPDM/ZS5idXR0b246MykpOnI9MSZlLmJ1dHRvbnM/MDo0JmUuYnV0dG9ucz8xOjImZS5idXR0b25zPzI6MzticmVhaztjYXNlIm1vdXNldXAiOmk9MCxyPWUuYnV0dG9uPDM/ZS5idXR0b246MzticmVhaztjYXNlIm1vdXNlZG93biI6aT0xLHI9ZS5idXR0b248Mz9lLmJ1dHRvbjozO2JyZWFrO2Nhc2Uid2hlZWwiOjAhPT1lLmRlbHRhWSYmKGk9ZS5kZWx0YVk8MD8wOjEpLHI9NDticmVhaztkZWZhdWx0OnJldHVybiExfXJldHVybiEodm9pZCAwPT09aXx8dm9pZCAwPT09cnx8cj40KSYmdC5jb3JlTW91c2VTZXJ2aWNlLnRyaWdnZXJNb3VzZUV2ZW50KHtjb2w6bi54LTMzLHJvdzpuLnktMzMsYnV0dG9uOnIsYWN0aW9uOmksY3RybDplLmN0cmxLZXksYWx0OmUuYWx0S2V5LHNoaWZ0OmUuc2hpZnRLZXl9KX12YXIgbj17bW91c2V1cDpudWxsLHdoZWVsOm51bGwsbW91c2VkcmFnOm51bGwsbW91c2Vtb3ZlOm51bGx9LG89ZnVuY3Rpb24odCl7cmV0dXJuIGkodCksdC5idXR0b25zfHwoZS5fZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2V1cCIsbi5tb3VzZXVwKSxuLm1vdXNlZHJhZyYmZS5fZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2Vtb3ZlIixuLm1vdXNlZHJhZykpLGUuY2FuY2VsKHQpfSxzPWZ1bmN0aW9uKHQpe3JldHVybiBpKHQpLGUuY2FuY2VsKHQsITApfSxhPWZ1bmN0aW9uKGUpe2UuYnV0dG9ucyYmaShlKX0sbD1mdW5jdGlvbihlKXtlLmJ1dHRvbnN8fGkoZSl9O3RoaXMucmVnaXN0ZXIodGhpcy5jb3JlTW91c2VTZXJ2aWNlLm9uUHJvdG9jb2xDaGFuZ2UoKGZ1bmN0aW9uKHQpe3Q/KCJkZWJ1ZyI9PT1lLm9wdGlvbnNTZXJ2aWNlLm9wdGlvbnMubG9nTGV2ZWwmJmUuX2xvZ1NlcnZpY2UuZGVidWcoIkJpbmRpbmcgdG8gbW91c2UgZXZlbnRzOiIsZS5jb3JlTW91c2VTZXJ2aWNlLmV4cGxhaW5FdmVudHModCkpLGUuZWxlbWVudC5jbGFzc0xpc3QuYWRkKCJlbmFibGUtbW91c2UtZXZlbnRzIiksZS5fc2VsZWN0aW9uU2VydmljZS5kaXNhYmxlKCkpOihlLl9sb2dTZXJ2aWNlLmRlYnVnKCJVbmJpbmRpbmcgZnJvbSBtb3VzZSBldmVudHMuIiksZS5lbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoImVuYWJsZS1tb3VzZS1ldmVudHMiKSxlLl9zZWxlY3Rpb25TZXJ2aWNlLmVuYWJsZSgpKSw4JnQ/bi5tb3VzZW1vdmV8fChyLmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNlbW92ZSIsbCksbi5tb3VzZW1vdmU9bCk6KHIucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2Vtb3ZlIixuLm1vdXNlbW92ZSksbi5tb3VzZW1vdmU9bnVsbCksMTYmdD9uLndoZWVsfHwoci5hZGRFdmVudExpc3RlbmVyKCJ3aGVlbCIscyx7cGFzc2l2ZTohMX0pLG4ud2hlZWw9cyk6KHIucmVtb3ZlRXZlbnRMaXN0ZW5lcigid2hlZWwiLG4ud2hlZWwpLG4ud2hlZWw9bnVsbCksMiZ0P24ubW91c2V1cHx8KG4ubW91c2V1cD1vKTooZS5fZG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2V1cCIsbi5tb3VzZXVwKSxuLm1vdXNldXA9bnVsbCksNCZ0P24ubW91c2VkcmFnfHwobi5tb3VzZWRyYWc9YSk6KGUuX2RvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoIm1vdXNlbW92ZSIsbi5tb3VzZWRyYWcpLG4ubW91c2VkcmFnPW51bGwpfSkpKSx0aGlzLmNvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2w9dGhpcy5jb3JlTW91c2VTZXJ2aWNlLmFjdGl2ZVByb3RvY29sLHRoaXMucmVnaXN0ZXIoKDAsZC5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHIsIm1vdXNlZG93biIsKGZ1bmN0aW9uKHQpe2lmKHQucHJldmVudERlZmF1bHQoKSxlLmZvY3VzKCksZS5jb3JlTW91c2VTZXJ2aWNlLmFyZU1vdXNlRXZlbnRzQWN0aXZlJiYhZS5fc2VsZWN0aW9uU2VydmljZS5zaG91bGRGb3JjZVNlbGVjdGlvbih0KSlyZXR1cm4gaSh0KSxuLm1vdXNldXAmJmUuX2RvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNldXAiLG4ubW91c2V1cCksbi5tb3VzZWRyYWcmJmUuX2RvY3VtZW50LmFkZEV2ZW50TGlzdGVuZXIoIm1vdXNlbW92ZSIsbi5tb3VzZWRyYWcpLGUuY2FuY2VsKHQpfSkpKSx0aGlzLnJlZ2lzdGVyKCgwLGQuYWRkRGlzcG9zYWJsZURvbUxpc3RlbmVyKShyLCJ3aGVlbCIsKGZ1bmN0aW9uKHQpe2lmKCFuLndoZWVsKXtpZighZS5idWZmZXIuaGFzU2Nyb2xsYmFjayl7dmFyIHI9ZS52aWV3cG9ydC5nZXRMaW5lc1Njcm9sbGVkKHQpO2lmKDA9PT1yKXJldHVybjtmb3IodmFyIGk9Yy5DMC5FU0MrKGUuY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmFwcGxpY2F0aW9uQ3Vyc29yS2V5cz8iTyI6IlsiKSsodC5kZWx0YVk8MD8iQSI6IkIiKSxvPSIiLHM9MDtzPE1hdGguYWJzKHIpO3MrKylvKz1pO3JldHVybiBlLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQobywhMCksZS5jYW5jZWwodCwhMCl9cmV0dXJuIGUudmlld3BvcnQub25XaGVlbCh0KT9lLmNhbmNlbCh0KTp2b2lkIDB9fSkse3Bhc3NpdmU6ITF9KSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikociwidG91Y2hzdGFydCIsKGZ1bmN0aW9uKHQpe2lmKCFlLmNvcmVNb3VzZVNlcnZpY2UuYXJlTW91c2VFdmVudHNBY3RpdmUpcmV0dXJuIGUudmlld3BvcnQub25Ub3VjaFN0YXJ0KHQpLGUuY2FuY2VsKHQpfSkse3Bhc3NpdmU6ITB9KSksdGhpcy5yZWdpc3RlcigoMCxkLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikociwidG91Y2htb3ZlIiwoZnVuY3Rpb24odCl7aWYoIWUuY29yZU1vdXNlU2VydmljZS5hcmVNb3VzZUV2ZW50c0FjdGl2ZSlyZXR1cm4gZS52aWV3cG9ydC5vblRvdWNoTW92ZSh0KT92b2lkIDA6ZS5jYW5jZWwodCl9KSx7cGFzc2l2ZTohMX0pKX0sdC5wcm90b3R5cGUucmVmcmVzaD1mdW5jdGlvbihlLHQpe3ZhciByO251bGw9PT0ocj10aGlzLl9yZW5kZXJTZXJ2aWNlKXx8dm9pZCAwPT09cnx8ci5yZWZyZXNoUm93cyhlLHQpfSx0LnByb3RvdHlwZS5fcXVldWVMaW5raWZpY2F0aW9uPWZ1bmN0aW9uKGUsdCl7dmFyIHI7bnVsbD09PShyPXRoaXMubGlua2lmaWVyKXx8dm9pZCAwPT09cnx8ci5saW5raWZ5Um93cyhlLHQpfSx0LnByb3RvdHlwZS51cGRhdGVDdXJzb3JTdHlsZT1mdW5jdGlvbihlKXt2YXIgdDsobnVsbD09PSh0PXRoaXMuX3NlbGVjdGlvblNlcnZpY2UpfHx2b2lkIDA9PT10P3ZvaWQgMDp0LnNob3VsZENvbHVtblNlbGVjdChlKSk/dGhpcy5lbGVtZW50LmNsYXNzTGlzdC5hZGQoImNvbHVtbi1zZWxlY3QiKTp0aGlzLmVsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZSgiY29sdW1uLXNlbGVjdCIpfSx0LnByb3RvdHlwZS5fc2hvd0N1cnNvcj1mdW5jdGlvbigpe3RoaXMuY29yZVNlcnZpY2UuaXNDdXJzb3JJbml0aWFsaXplZHx8KHRoaXMuY29yZVNlcnZpY2UuaXNDdXJzb3JJbml0aWFsaXplZD0hMCx0aGlzLnJlZnJlc2godGhpcy5idWZmZXIueSx0aGlzLmJ1ZmZlci55KSl9LHQucHJvdG90eXBlLnNjcm9sbExpbmVzPWZ1bmN0aW9uKHQscixpKXt2b2lkIDA9PT1pJiYoaT0wKSxlLnByb3RvdHlwZS5zY3JvbGxMaW5lcy5jYWxsKHRoaXMsdCxyLGkpLHRoaXMucmVmcmVzaCgwLHRoaXMucm93cy0xKX0sdC5wcm90b3R5cGUucGFzdGU9ZnVuY3Rpb24oZSl7KDAsYS5wYXN0ZSkoZSx0aGlzLnRleHRhcmVhLHRoaXMuY29yZVNlcnZpY2UpfSx0LnByb3RvdHlwZS5hdHRhY2hDdXN0b21LZXlFdmVudEhhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5fY3VzdG9tS2V5RXZlbnRIYW5kbGVyPWV9LHQucHJvdG90eXBlLnJlZ2lzdGVyTGlua01hdGNoZXI9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMubGlua2lmaWVyLnJlZ2lzdGVyTGlua01hdGNoZXIoZSx0LHIpO3JldHVybiB0aGlzLnJlZnJlc2goMCx0aGlzLnJvd3MtMSksaX0sdC5wcm90b3R5cGUuZGVyZWdpc3RlckxpbmtNYXRjaGVyPWZ1bmN0aW9uKGUpe3RoaXMubGlua2lmaWVyLmRlcmVnaXN0ZXJMaW5rTWF0Y2hlcihlKSYmdGhpcy5yZWZyZXNoKDAsdGhpcy5yb3dzLTEpfSx0LnByb3RvdHlwZS5yZWdpc3RlckxpbmtQcm92aWRlcj1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5saW5raWZpZXIyLnJlZ2lzdGVyTGlua1Byb3ZpZGVyKGUpfSx0LnByb3RvdHlwZS5yZWdpc3RlckNoYXJhY3RlckpvaW5lcj1mdW5jdGlvbihlKXtpZighdGhpcy5fY2hhcmFjdGVySm9pbmVyU2VydmljZSl0aHJvdyBuZXcgRXJyb3IoIlRlcm1pbmFsIG11c3QgYmUgb3BlbmVkIGZpcnN0Iik7dmFyIHQ9dGhpcy5fY2hhcmFjdGVySm9pbmVyU2VydmljZS5yZWdpc3RlcihlKTtyZXR1cm4gdGhpcy5yZWZyZXNoKDAsdGhpcy5yb3dzLTEpLHR9LHQucHJvdG90eXBlLmRlcmVnaXN0ZXJDaGFyYWN0ZXJKb2luZXI9ZnVuY3Rpb24oZSl7aWYoIXRoaXMuX2NoYXJhY3RlckpvaW5lclNlcnZpY2UpdGhyb3cgbmV3IEVycm9yKCJUZXJtaW5hbCBtdXN0IGJlIG9wZW5lZCBmaXJzdCIpO3RoaXMuX2NoYXJhY3RlckpvaW5lclNlcnZpY2UuZGVyZWdpc3RlcihlKSYmdGhpcy5yZWZyZXNoKDAsdGhpcy5yb3dzLTEpfSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm1hcmtlcnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5idWZmZXIubWFya2Vyc30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSx0LnByb3RvdHlwZS5hZGRNYXJrZXI9ZnVuY3Rpb24oZSl7aWYodGhpcy5idWZmZXI9PT10aGlzLmJ1ZmZlcnMubm9ybWFsKXJldHVybiB0aGlzLmJ1ZmZlci5hZGRNYXJrZXIodGhpcy5idWZmZXIueWJhc2UrdGhpcy5idWZmZXIueStlKX0sdC5wcm90b3R5cGUuaGFzU2VsZWN0aW9uPWZ1bmN0aW9uKCl7cmV0dXJuISF0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlJiZ0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLmhhc1NlbGVjdGlvbn0sdC5wcm90b3R5cGUuc2VsZWN0PWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLnNldFNlbGVjdGlvbihlLHQscil9LHQucHJvdG90eXBlLmdldFNlbGVjdGlvbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlP3RoaXMuX3NlbGVjdGlvblNlcnZpY2Uuc2VsZWN0aW9uVGV4dDoiIn0sdC5wcm90b3R5cGUuZ2V0U2VsZWN0aW9uUG9zaXRpb249ZnVuY3Rpb24oKXtpZih0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlJiZ0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLmhhc1NlbGVjdGlvbilyZXR1cm57c3RhcnRDb2x1bW46dGhpcy5fc2VsZWN0aW9uU2VydmljZS5zZWxlY3Rpb25TdGFydFswXSxzdGFydFJvdzp0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLnNlbGVjdGlvblN0YXJ0WzFdLGVuZENvbHVtbjp0aGlzLl9zZWxlY3Rpb25TZXJ2aWNlLnNlbGVjdGlvbkVuZFswXSxlbmRSb3c6dGhpcy5fc2VsZWN0aW9uU2VydmljZS5zZWxlY3Rpb25FbmRbMV19fSx0LnByb3RvdHlwZS5jbGVhclNlbGVjdGlvbj1mdW5jdGlvbigpe3ZhciBlO251bGw9PT0oZT10aGlzLl9zZWxlY3Rpb25TZXJ2aWNlKXx8dm9pZCAwPT09ZXx8ZS5jbGVhclNlbGVjdGlvbigpfSx0LnByb3RvdHlwZS5zZWxlY3RBbGw9ZnVuY3Rpb24oKXt2YXIgZTtudWxsPT09KGU9dGhpcy5fc2VsZWN0aW9uU2VydmljZSl8fHZvaWQgMD09PWV8fGUuc2VsZWN0QWxsKCl9LHQucHJvdG90eXBlLnNlbGVjdExpbmVzPWZ1bmN0aW9uKGUsdCl7dmFyIHI7bnVsbD09PShyPXRoaXMuX3NlbGVjdGlvblNlcnZpY2UpfHx2b2lkIDA9PT1yfHxyLnNlbGVjdExpbmVzKGUsdCl9LHQucHJvdG90eXBlLl9rZXlEb3duPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2tleURvd25IYW5kbGVkPSExLHRoaXMuX2N1c3RvbUtleUV2ZW50SGFuZGxlciYmITE9PT10aGlzLl9jdXN0b21LZXlFdmVudEhhbmRsZXIoZSkpcmV0dXJuITE7aWYoIXRoaXMuX2NvbXBvc2l0aW9uSGVscGVyLmtleWRvd24oZSkpcmV0dXJuIHRoaXMuYnVmZmVyLnliYXNlIT09dGhpcy5idWZmZXIueWRpc3AmJnRoaXMuX2J1ZmZlclNlcnZpY2Uuc2Nyb2xsVG9Cb3R0b20oKSwhMTsiRGVhZCIhPT1lLmtleSYmIkFsdEdyYXBoIiE9PWUua2V5fHwodGhpcy5fdW5wcm9jZXNzZWREZWFkS2V5PSEwKTt2YXIgdD0oMCxiLmV2YWx1YXRlS2V5Ym9hcmRFdmVudCkoZSx0aGlzLmNvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5hcHBsaWNhdGlvbkN1cnNvcktleXMsdGhpcy5icm93c2VyLmlzTWFjLHRoaXMub3B0aW9ucy5tYWNPcHRpb25Jc01ldGEpO2lmKHRoaXMudXBkYXRlQ3Vyc29yU3R5bGUoZSksMz09PXQudHlwZXx8Mj09PXQudHlwZSl7dmFyIHI9dGhpcy5yb3dzLTE7cmV0dXJuIHRoaXMuc2Nyb2xsTGluZXMoMj09PXQudHlwZT8tcjpyKSx0aGlzLmNhbmNlbChlLCEwKX1yZXR1cm4gMT09PXQudHlwZSYmdGhpcy5zZWxlY3RBbGwoKSwhIXRoaXMuX2lzVGhpcmRMZXZlbFNoaWZ0KHRoaXMuYnJvd3NlcixlKXx8KHQuY2FuY2VsJiZ0aGlzLmNhbmNlbChlLCEwKSwhdC5rZXl8fCh0aGlzLl91bnByb2Nlc3NlZERlYWRLZXk/KHRoaXMuX3VucHJvY2Vzc2VkRGVhZEtleT0hMSwhMCk6KHQua2V5IT09Yy5DMC5FVFgmJnQua2V5IT09Yy5DMC5DUnx8KHRoaXMudGV4dGFyZWEudmFsdWU9IiIpLHRoaXMuX29uS2V5LmZpcmUoe2tleTp0LmtleSxkb21FdmVudDplfSksdGhpcy5fc2hvd0N1cnNvcigpLHRoaXMuY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudCh0LmtleSwhMCksdGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcmVlblJlYWRlck1vZGU/dm9pZCh0aGlzLl9rZXlEb3duSGFuZGxlZD0hMCk6dGhpcy5jYW5jZWwoZSwhMCkpKSl9LHQucHJvdG90eXBlLl9pc1RoaXJkTGV2ZWxTaGlmdD1mdW5jdGlvbihlLHQpe3ZhciByPWUuaXNNYWMmJiF0aGlzLm9wdGlvbnMubWFjT3B0aW9uSXNNZXRhJiZ0LmFsdEtleSYmIXQuY3RybEtleSYmIXQubWV0YUtleXx8ZS5pc1dpbmRvd3MmJnQuYWx0S2V5JiZ0LmN0cmxLZXkmJiF0Lm1ldGFLZXl8fGUuaXNXaW5kb3dzJiZ0LmdldE1vZGlmaWVyU3RhdGUoIkFsdEdyYXBoIik7cmV0dXJuImtleXByZXNzIj09PXQudHlwZT9yOnImJighdC5rZXlDb2RlfHx0LmtleUNvZGU+NDcpfSx0LnByb3RvdHlwZS5fa2V5VXA9ZnVuY3Rpb24oZSl7dGhpcy5fY3VzdG9tS2V5RXZlbnRIYW5kbGVyJiYhMT09PXRoaXMuX2N1c3RvbUtleUV2ZW50SGFuZGxlcihlKXx8KGZ1bmN0aW9uKGUpe3JldHVybiAxNj09PWUua2V5Q29kZXx8MTc9PT1lLmtleUNvZGV8fDE4PT09ZS5rZXlDb2RlfShlKXx8dGhpcy5mb2N1cygpLHRoaXMudXBkYXRlQ3Vyc29yU3R5bGUoZSksdGhpcy5fa2V5UHJlc3NIYW5kbGVkPSExKX0sdC5wcm90b3R5cGUuX2tleVByZXNzPWZ1bmN0aW9uKGUpe3ZhciB0O2lmKHRoaXMuX2tleVByZXNzSGFuZGxlZD0hMSx0aGlzLl9rZXlEb3duSGFuZGxlZClyZXR1cm4hMTtpZih0aGlzLl9jdXN0b21LZXlFdmVudEhhbmRsZXImJiExPT09dGhpcy5fY3VzdG9tS2V5RXZlbnRIYW5kbGVyKGUpKXJldHVybiExO2lmKHRoaXMuY2FuY2VsKGUpLGUuY2hhckNvZGUpdD1lLmNoYXJDb2RlO2Vsc2UgaWYobnVsbD09PWUud2hpY2h8fHZvaWQgMD09PWUud2hpY2gpdD1lLmtleUNvZGU7ZWxzZXtpZigwPT09ZS53aGljaHx8MD09PWUuY2hhckNvZGUpcmV0dXJuITE7dD1lLndoaWNofXJldHVybiEoIXR8fChlLmFsdEtleXx8ZS5jdHJsS2V5fHxlLm1ldGFLZXkpJiYhdGhpcy5faXNUaGlyZExldmVsU2hpZnQodGhpcy5icm93c2VyLGUpfHwodD1TdHJpbmcuZnJvbUNoYXJDb2RlKHQpLHRoaXMuX29uS2V5LmZpcmUoe2tleTp0LGRvbUV2ZW50OmV9KSx0aGlzLl9zaG93Q3Vyc29yKCksdGhpcy5jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHQsITApLHRoaXMuX2tleVByZXNzSGFuZGxlZD0hMCx0aGlzLl91bnByb2Nlc3NlZERlYWRLZXk9ITEsMCkpfSx0LnByb3RvdHlwZS5faW5wdXRFdmVudD1mdW5jdGlvbihlKXtpZihlLmRhdGEmJiJpbnNlcnRUZXh0Ij09PWUuaW5wdXRUeXBlJiYhZS5jb21wb3NlZCYmIXRoaXMub3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5zY3JlZW5SZWFkZXJNb2RlKXtpZih0aGlzLl9rZXlQcmVzc0hhbmRsZWQpcmV0dXJuITE7dGhpcy5fdW5wcm9jZXNzZWREZWFkS2V5PSExO3ZhciB0PWUuZGF0YTtyZXR1cm4gdGhpcy5jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHQsITApLHRoaXMuY2FuY2VsKGUpLCEwfXJldHVybiExfSx0LnByb3RvdHlwZS5iZWxsPWZ1bmN0aW9uKCl7dmFyIGU7dGhpcy5fc291bmRCZWxsKCkmJihudWxsPT09KGU9dGhpcy5fc291bmRTZXJ2aWNlKXx8dm9pZCAwPT09ZXx8ZS5wbGF5QmVsbFNvdW5kKCkpLHRoaXMuX29uQmVsbC5maXJlKCl9LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbih0LHIpe3QhPT10aGlzLmNvbHN8fHIhPT10aGlzLnJvd3M/ZS5wcm90b3R5cGUucmVzaXplLmNhbGwodGhpcyx0LHIpOnRoaXMuX2NoYXJTaXplU2VydmljZSYmIXRoaXMuX2NoYXJTaXplU2VydmljZS5oYXNWYWxpZFNpemUmJnRoaXMuX2NoYXJTaXplU2VydmljZS5tZWFzdXJlKCl9LHQucHJvdG90eXBlLl9hZnRlclJlc2l6ZT1mdW5jdGlvbihlLHQpe3ZhciByLGk7bnVsbD09PShyPXRoaXMuX2NoYXJTaXplU2VydmljZSl8fHZvaWQgMD09PXJ8fHIubWVhc3VyZSgpLG51bGw9PT0oaT10aGlzLnZpZXdwb3J0KXx8dm9pZCAwPT09aXx8aS5zeW5jU2Nyb2xsQXJlYSghMCl9LHQucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7aWYoMCE9PXRoaXMuYnVmZmVyLnliYXNlfHwwIT09dGhpcy5idWZmZXIueSl7dGhpcy5idWZmZXIubGluZXMuc2V0KDAsdGhpcy5idWZmZXIubGluZXMuZ2V0KHRoaXMuYnVmZmVyLnliYXNlK3RoaXMuYnVmZmVyLnkpKSx0aGlzLmJ1ZmZlci5saW5lcy5sZW5ndGg9MSx0aGlzLmJ1ZmZlci55ZGlzcD0wLHRoaXMuYnVmZmVyLnliYXNlPTAsdGhpcy5idWZmZXIueT0wO2Zvcih2YXIgZT0xO2U8dGhpcy5yb3dzO2UrKyl0aGlzLmJ1ZmZlci5saW5lcy5wdXNoKHRoaXMuYnVmZmVyLmdldEJsYW5rTGluZShDLkRFRkFVTFRfQVRUUl9EQVRBKSk7dGhpcy5yZWZyZXNoKDAsdGhpcy5yb3dzLTEpLHRoaXMuX29uU2Nyb2xsLmZpcmUoe3Bvc2l0aW9uOnRoaXMuYnVmZmVyLnlkaXNwLHNvdXJjZTowfSl9fSx0LnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe3ZhciB0LHI7dGhpcy5vcHRpb25zLnJvd3M9dGhpcy5yb3dzLHRoaXMub3B0aW9ucy5jb2xzPXRoaXMuY29sczt2YXIgaT10aGlzLl9jdXN0b21LZXlFdmVudEhhbmRsZXI7dGhpcy5fc2V0dXAoKSxlLnByb3RvdHlwZS5yZXNldC5jYWxsKHRoaXMpLG51bGw9PT0odD10aGlzLl9zZWxlY3Rpb25TZXJ2aWNlKXx8dm9pZCAwPT09dHx8dC5yZXNldCgpLHRoaXMuX2N1c3RvbUtleUV2ZW50SGFuZGxlcj1pLHRoaXMucmVmcmVzaCgwLHRoaXMucm93cy0xKSxudWxsPT09KHI9dGhpcy52aWV3cG9ydCl8fHZvaWQgMD09PXJ8fHIuc3luY1Njcm9sbEFyZWEoKX0sdC5wcm90b3R5cGUuY2xlYXJUZXh0dXJlQXRsYXM9ZnVuY3Rpb24oKXt2YXIgZTtudWxsPT09KGU9dGhpcy5fcmVuZGVyU2VydmljZSl8fHZvaWQgMD09PWV8fGUuY2xlYXJUZXh0dXJlQXRsYXMoKX0sdC5wcm90b3R5cGUuX3JlcG9ydEZvY3VzPWZ1bmN0aW9uKCl7dmFyIGU7KG51bGw9PT0oZT10aGlzLmVsZW1lbnQpfHx2b2lkIDA9PT1lP3ZvaWQgMDplLmNsYXNzTGlzdC5jb250YWlucygiZm9jdXMiKSk/dGhpcy5jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KGMuQzAuRVNDKyJbSSIpOnRoaXMuY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChjLkMwLkVTQysiW08iKX0sdC5wcm90b3R5cGUuX3JlcG9ydFdpbmRvd3NPcHRpb25zPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX3JlbmRlclNlcnZpY2Upc3dpdGNoKGUpe2Nhc2UgbC5XaW5kb3dzT3B0aW9uc1JlcG9ydFR5cGUuR0VUX1dJTl9TSVpFX1BJWEVMUzp2YXIgdD10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuc2NhbGVkQ2FudmFzV2lkdGgudG9GaXhlZCgwKSxyPXRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5zY2FsZWRDYW52YXNIZWlnaHQudG9GaXhlZCgwKTt0aGlzLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoYy5DMC5FU0MrIls0OyIrcisiOyIrdCsidCIpO2JyZWFrO2Nhc2UgbC5XaW5kb3dzT3B0aW9uc1JlcG9ydFR5cGUuR0VUX0NFTExfU0laRV9QSVhFTFM6dmFyIGk9dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLnNjYWxlZENlbGxXaWR0aC50b0ZpeGVkKDApLG49dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLnNjYWxlZENlbGxIZWlnaHQudG9GaXhlZCgwKTt0aGlzLmNvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoYy5DMC5FU0MrIls2OyIrbisiOyIraSsidCIpfX0sdC5wcm90b3R5cGUuY2FuY2VsPWZ1bmN0aW9uKGUsdCl7aWYodGhpcy5vcHRpb25zLmNhbmNlbEV2ZW50c3x8dClyZXR1cm4gZS5wcmV2ZW50RGVmYXVsdCgpLGUuc3RvcFByb3BhZ2F0aW9uKCksITF9LHQucHJvdG90eXBlLl92aXN1YWxCZWxsPWZ1bmN0aW9uKCl7cmV0dXJuITF9LHQucHJvdG90eXBlLl9zb3VuZEJlbGw9ZnVuY3Rpb24oKXtyZXR1cm4ic291bmQiPT09dGhpcy5vcHRpb25zLmJlbGxTdHlsZX0sdH0oUi5Db3JlVGVybWluYWwpO3QuVGVybWluYWw9UH0sOTkyNDooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlRpbWVCYXNlZERlYm91bmNlcj12b2lkIDA7dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCl7dm9pZCAwPT09dCYmKHQ9MWUzKSx0aGlzLl9yZW5kZXJDYWxsYmFjaz1lLHRoaXMuX2RlYm91bmNlVGhyZXNob2xkTVM9dCx0aGlzLl9sYXN0UmVmcmVzaE1zPTAsdGhpcy5fYWRkaXRpb25hbFJlZnJlc2hSZXF1ZXN0ZWQ9ITF9cmV0dXJuIGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt0aGlzLl9yZWZyZXNoVGltZW91dElEJiZjbGVhclRpbWVvdXQodGhpcy5fcmVmcmVzaFRpbWVvdXRJRCl9LGUucHJvdG90eXBlLnJlZnJlc2g9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXM7dGhpcy5fcm93Q291bnQ9cixlPXZvaWQgMCE9PWU/ZTowLHQ9dm9pZCAwIT09dD90OnRoaXMuX3Jvd0NvdW50LTEsdGhpcy5fcm93U3RhcnQ9dm9pZCAwIT09dGhpcy5fcm93U3RhcnQ/TWF0aC5taW4odGhpcy5fcm93U3RhcnQsZSk6ZSx0aGlzLl9yb3dFbmQ9dm9pZCAwIT09dGhpcy5fcm93RW5kP01hdGgubWF4KHRoaXMuX3Jvd0VuZCx0KTp0O3ZhciBuPURhdGUubm93KCk7aWYobi10aGlzLl9sYXN0UmVmcmVzaE1zPj10aGlzLl9kZWJvdW5jZVRocmVzaG9sZE1TKXRoaXMuX2xhc3RSZWZyZXNoTXM9bix0aGlzLl9pbm5lclJlZnJlc2goKTtlbHNlIGlmKCF0aGlzLl9hZGRpdGlvbmFsUmVmcmVzaFJlcXVlc3RlZCl7dmFyIG89bi10aGlzLl9sYXN0UmVmcmVzaE1zLHM9dGhpcy5fZGVib3VuY2VUaHJlc2hvbGRNUy1vO3RoaXMuX2FkZGl0aW9uYWxSZWZyZXNoUmVxdWVzdGVkPSEwLHRoaXMuX3JlZnJlc2hUaW1lb3V0SUQ9d2luZG93LnNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7aS5fbGFzdFJlZnJlc2hNcz1EYXRlLm5vdygpLGkuX2lubmVyUmVmcmVzaCgpLGkuX2FkZGl0aW9uYWxSZWZyZXNoUmVxdWVzdGVkPSExLGkuX3JlZnJlc2hUaW1lb3V0SUQ9dm9pZCAwfSkscyl9fSxlLnByb3RvdHlwZS5faW5uZXJSZWZyZXNoPWZ1bmN0aW9uKCl7aWYodm9pZCAwIT09dGhpcy5fcm93U3RhcnQmJnZvaWQgMCE9PXRoaXMuX3Jvd0VuZCYmdm9pZCAwIT09dGhpcy5fcm93Q291bnQpe3ZhciBlPU1hdGgubWF4KHRoaXMuX3Jvd1N0YXJ0LDApLHQ9TWF0aC5taW4odGhpcy5fcm93RW5kLHRoaXMuX3Jvd0NvdW50LTEpO3RoaXMuX3Jvd1N0YXJ0PXZvaWQgMCx0aGlzLl9yb3dFbmQ9dm9pZCAwLHRoaXMuX3JlbmRlckNhbGxiYWNrKGUsdCl9fSxlfSgpO3QuVGltZUJhc2VkRGVib3VuY2VyPXJ9LDE2ODA6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSksbz10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LHM9dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuVmlld3BvcnQ9dm9pZCAwO3ZhciBhPXIoODQ0KSxjPXIoMzY1NiksbD1yKDQ3MjUpLHU9cigyNTg1KSxoPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyLGksbixvLHMsYSxsKXt2YXIgdT1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIHUuX3Njcm9sbExpbmVzPXQsdS5fdmlld3BvcnRFbGVtZW50PXIsdS5fc2Nyb2xsQXJlYT1pLHUuX2VsZW1lbnQ9bix1Ll9idWZmZXJTZXJ2aWNlPW8sdS5fb3B0aW9uc1NlcnZpY2U9cyx1Ll9jaGFyU2l6ZVNlcnZpY2U9YSx1Ll9yZW5kZXJTZXJ2aWNlPWwsdS5zY3JvbGxCYXJXaWR0aD0wLHUuX2N1cnJlbnRSb3dIZWlnaHQ9MCx1Ll9jdXJyZW50U2NhbGVkQ2VsbEhlaWdodD0wLHUuX2xhc3RSZWNvcmRlZEJ1ZmZlckxlbmd0aD0wLHUuX2xhc3RSZWNvcmRlZFZpZXdwb3J0SGVpZ2h0PTAsdS5fbGFzdFJlY29yZGVkQnVmZmVySGVpZ2h0PTAsdS5fbGFzdFRvdWNoWT0wLHUuX2xhc3RTY3JvbGxUb3A9MCx1Ll9sYXN0SGFkU2Nyb2xsQmFyPSExLHUuX3doZWVsUGFydGlhbFNjcm9sbD0wLHUuX3JlZnJlc2hBbmltYXRpb25GcmFtZT1udWxsLHUuX2lnbm9yZU5leHRTY3JvbGxFdmVudD0hMSx1LnNjcm9sbEJhcldpZHRoPXUuX3ZpZXdwb3J0RWxlbWVudC5vZmZzZXRXaWR0aC11Ll9zY3JvbGxBcmVhLm9mZnNldFdpZHRofHwxNSx1Ll9sYXN0SGFkU2Nyb2xsQmFyPSEwLHUucmVnaXN0ZXIoKDAsYy5hZGREaXNwb3NhYmxlRG9tTGlzdGVuZXIpKHUuX3ZpZXdwb3J0RWxlbWVudCwic2Nyb2xsIix1Ll9vblNjcm9sbC5iaW5kKHUpKSksdS5fYWN0aXZlQnVmZmVyPXUuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLHUucmVnaXN0ZXIodS5fYnVmZmVyU2VydmljZS5idWZmZXJzLm9uQnVmZmVyQWN0aXZhdGUoKGZ1bmN0aW9uKGUpe3JldHVybiB1Ll9hY3RpdmVCdWZmZXI9ZS5hY3RpdmVCdWZmZXJ9KSkpLHUuX3JlbmRlckRpbWVuc2lvbnM9dS5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLHUucmVnaXN0ZXIodS5fcmVuZGVyU2VydmljZS5vbkRpbWVuc2lvbnNDaGFuZ2UoKGZ1bmN0aW9uKGUpe3JldHVybiB1Ll9yZW5kZXJEaW1lbnNpb25zPWV9KSkpLHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIHUuc3luY1Njcm9sbEFyZWEoKX0pLDApLHV9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5vblRoZW1lQ2hhbmdlPWZ1bmN0aW9uKGUpe3RoaXMuX3ZpZXdwb3J0RWxlbWVudC5zdHlsZS5iYWNrZ3JvdW5kQ29sb3I9ZS5iYWNrZ3JvdW5kLmNzc30sdC5wcm90b3R5cGUuX3JlZnJlc2g9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcztpZihlKXJldHVybiB0aGlzLl9pbm5lclJlZnJlc2goKSx2b2lkKG51bGwhPT10aGlzLl9yZWZyZXNoQW5pbWF0aW9uRnJhbWUmJmNhbmNlbEFuaW1hdGlvbkZyYW1lKHRoaXMuX3JlZnJlc2hBbmltYXRpb25GcmFtZSkpO251bGw9PT10aGlzLl9yZWZyZXNoQW5pbWF0aW9uRnJhbWUmJih0aGlzLl9yZWZyZXNoQW5pbWF0aW9uRnJhbWU9cmVxdWVzdEFuaW1hdGlvbkZyYW1lKChmdW5jdGlvbigpe3JldHVybiB0Ll9pbm5lclJlZnJlc2goKX0pKSl9LHQucHJvdG90eXBlLl9pbm5lclJlZnJlc2g9ZnVuY3Rpb24oKXtpZih0aGlzLl9jaGFyU2l6ZVNlcnZpY2UuaGVpZ2h0PjApe3RoaXMuX2N1cnJlbnRSb3dIZWlnaHQ9dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLnNjYWxlZENlbGxIZWlnaHQvd2luZG93LmRldmljZVBpeGVsUmF0aW8sdGhpcy5fY3VycmVudFNjYWxlZENlbGxIZWlnaHQ9dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLnNjYWxlZENlbGxIZWlnaHQsdGhpcy5fbGFzdFJlY29yZGVkVmlld3BvcnRIZWlnaHQ9dGhpcy5fdmlld3BvcnRFbGVtZW50Lm9mZnNldEhlaWdodDt2YXIgZT1NYXRoLnJvdW5kKHRoaXMuX2N1cnJlbnRSb3dIZWlnaHQqdGhpcy5fbGFzdFJlY29yZGVkQnVmZmVyTGVuZ3RoKSsodGhpcy5fbGFzdFJlY29yZGVkVmlld3BvcnRIZWlnaHQtdGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLmNhbnZhc0hlaWdodCk7dGhpcy5fbGFzdFJlY29yZGVkQnVmZmVySGVpZ2h0IT09ZSYmKHRoaXMuX2xhc3RSZWNvcmRlZEJ1ZmZlckhlaWdodD1lLHRoaXMuX3Njcm9sbEFyZWEuc3R5bGUuaGVpZ2h0PXRoaXMuX2xhc3RSZWNvcmRlZEJ1ZmZlckhlaWdodCsicHgiKX12YXIgdD10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCp0aGlzLl9jdXJyZW50Um93SGVpZ2h0O3RoaXMuX3ZpZXdwb3J0RWxlbWVudC5zY3JvbGxUb3AhPT10JiYodGhpcy5faWdub3JlTmV4dFNjcm9sbEV2ZW50PSEwLHRoaXMuX3ZpZXdwb3J0RWxlbWVudC5zY3JvbGxUb3A9dCksMD09PXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuc2Nyb2xsYmFjaz90aGlzLnNjcm9sbEJhcldpZHRoPTA6dGhpcy5zY3JvbGxCYXJXaWR0aD10aGlzLl92aWV3cG9ydEVsZW1lbnQub2Zmc2V0V2lkdGgtdGhpcy5fc2Nyb2xsQXJlYS5vZmZzZXRXaWR0aHx8MTUsdGhpcy5fbGFzdEhhZFNjcm9sbEJhcj10aGlzLnNjcm9sbEJhcldpZHRoPjA7dmFyIHI9d2luZG93LmdldENvbXB1dGVkU3R5bGUodGhpcy5fZWxlbWVudCksaT1wYXJzZUludChyLnBhZGRpbmdMZWZ0KStwYXJzZUludChyLnBhZGRpbmdSaWdodCk7dGhpcy5fdmlld3BvcnRFbGVtZW50LnN0eWxlLndpZHRoPSh0aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoKnRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyt0aGlzLnNjcm9sbEJhcldpZHRoKyh0aGlzLl9sYXN0SGFkU2Nyb2xsQmFyP2k6MCkpLnRvU3RyaW5nKCkrInB4Iix0aGlzLl9yZWZyZXNoQW5pbWF0aW9uRnJhbWU9bnVsbH0sdC5wcm90b3R5cGUuc3luY1Njcm9sbEFyZWE9ZnVuY3Rpb24oZSl7aWYodm9pZCAwPT09ZSYmKGU9ITEpLHRoaXMuX2xhc3RSZWNvcmRlZEJ1ZmZlckxlbmd0aCE9PXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLmxpbmVzLmxlbmd0aClyZXR1cm4gdGhpcy5fbGFzdFJlY29yZGVkQnVmZmVyTGVuZ3RoPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLmxpbmVzLmxlbmd0aCx2b2lkIHRoaXMuX3JlZnJlc2goZSk7dGhpcy5fbGFzdFJlY29yZGVkVmlld3BvcnRIZWlnaHQ9PT10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0JiZ0aGlzLl9sYXN0U2Nyb2xsVG9wPT09dGhpcy5fYWN0aXZlQnVmZmVyLnlkaXNwKnRoaXMuX2N1cnJlbnRSb3dIZWlnaHQmJnRoaXMuX3JlbmRlckRpbWVuc2lvbnMuc2NhbGVkQ2VsbEhlaWdodD09PXRoaXMuX2N1cnJlbnRTY2FsZWRDZWxsSGVpZ2h0P3RoaXMuX2xhc3RIYWRTY3JvbGxCYXIhPT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcm9sbGJhY2s+MCYmdGhpcy5fcmVmcmVzaChlKTp0aGlzLl9yZWZyZXNoKGUpfSx0LnByb3RvdHlwZS5fb25TY3JvbGw9ZnVuY3Rpb24oZSl7aWYodGhpcy5fbGFzdFNjcm9sbFRvcD10aGlzLl92aWV3cG9ydEVsZW1lbnQuc2Nyb2xsVG9wLHRoaXMuX3ZpZXdwb3J0RWxlbWVudC5vZmZzZXRQYXJlbnQpe2lmKHRoaXMuX2lnbm9yZU5leHRTY3JvbGxFdmVudClyZXR1cm4gdGhpcy5faWdub3JlTmV4dFNjcm9sbEV2ZW50PSExLHZvaWQgdGhpcy5fc2Nyb2xsTGluZXMoMCk7dmFyIHQ9TWF0aC5yb3VuZCh0aGlzLl9sYXN0U2Nyb2xsVG9wL3RoaXMuX2N1cnJlbnRSb3dIZWlnaHQpLXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwO3RoaXMuX3Njcm9sbExpbmVzKHQpfX0sdC5wcm90b3R5cGUuX2J1YmJsZVNjcm9sbD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX3ZpZXdwb3J0RWxlbWVudC5zY3JvbGxUb3ArdGhpcy5fbGFzdFJlY29yZGVkVmlld3BvcnRIZWlnaHQ7cmV0dXJuISh0PDAmJjAhPT10aGlzLl92aWV3cG9ydEVsZW1lbnQuc2Nyb2xsVG9wfHx0PjAmJnI8dGhpcy5fbGFzdFJlY29yZGVkQnVmZmVySGVpZ2h0KXx8KGUuY2FuY2VsYWJsZSYmZS5wcmV2ZW50RGVmYXVsdCgpLCExKX0sdC5wcm90b3R5cGUub25XaGVlbD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9nZXRQaXhlbHNTY3JvbGxlZChlKTtyZXR1cm4gMCE9PXQmJih0aGlzLl92aWV3cG9ydEVsZW1lbnQuc2Nyb2xsVG9wKz10LHRoaXMuX2J1YmJsZVNjcm9sbChlLHQpKX0sdC5wcm90b3R5cGUuX2dldFBpeGVsc1Njcm9sbGVkPWZ1bmN0aW9uKGUpe2lmKDA9PT1lLmRlbHRhWXx8ZS5zaGlmdEtleSlyZXR1cm4gMDt2YXIgdD10aGlzLl9hcHBseVNjcm9sbE1vZGlmaWVyKGUuZGVsdGFZLGUpO3JldHVybiBlLmRlbHRhTW9kZT09PVdoZWVsRXZlbnQuRE9NX0RFTFRBX0xJTkU/dCo9dGhpcy5fY3VycmVudFJvd0hlaWdodDplLmRlbHRhTW9kZT09PVdoZWVsRXZlbnQuRE9NX0RFTFRBX1BBR0UmJih0Kj10aGlzLl9jdXJyZW50Um93SGVpZ2h0KnRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyksdH0sdC5wcm90b3R5cGUuZ2V0TGluZXNTY3JvbGxlZD1mdW5jdGlvbihlKXtpZigwPT09ZS5kZWx0YVl8fGUuc2hpZnRLZXkpcmV0dXJuIDA7dmFyIHQ9dGhpcy5fYXBwbHlTY3JvbGxNb2RpZmllcihlLmRlbHRhWSxlKTtyZXR1cm4gZS5kZWx0YU1vZGU9PT1XaGVlbEV2ZW50LkRPTV9ERUxUQV9QSVhFTD8odC89dGhpcy5fY3VycmVudFJvd0hlaWdodCswLHRoaXMuX3doZWVsUGFydGlhbFNjcm9sbCs9dCx0PU1hdGguZmxvb3IoTWF0aC5hYnModGhpcy5fd2hlZWxQYXJ0aWFsU2Nyb2xsKSkqKHRoaXMuX3doZWVsUGFydGlhbFNjcm9sbD4wPzE6LTEpLHRoaXMuX3doZWVsUGFydGlhbFNjcm9sbCU9MSk6ZS5kZWx0YU1vZGU9PT1XaGVlbEV2ZW50LkRPTV9ERUxUQV9QQUdFJiYodCo9dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKSx0fSx0LnByb3RvdHlwZS5fYXBwbHlTY3JvbGxNb2RpZmllcj1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZmFzdFNjcm9sbE1vZGlmaWVyO3JldHVybiJhbHQiPT09ciYmdC5hbHRLZXl8fCJjdHJsIj09PXImJnQuY3RybEtleXx8InNoaWZ0Ij09PXImJnQuc2hpZnRLZXk/ZSp0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZhc3RTY3JvbGxTZW5zaXRpdml0eSp0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcm9sbFNlbnNpdGl2aXR5OmUqdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5zY3JvbGxTZW5zaXRpdml0eX0sdC5wcm90b3R5cGUub25Ub3VjaFN0YXJ0PWZ1bmN0aW9uKGUpe3RoaXMuX2xhc3RUb3VjaFk9ZS50b3VjaGVzWzBdLnBhZ2VZfSx0LnByb3RvdHlwZS5vblRvdWNoTW92ZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9sYXN0VG91Y2hZLWUudG91Y2hlc1swXS5wYWdlWTtyZXR1cm4gdGhpcy5fbGFzdFRvdWNoWT1lLnRvdWNoZXNbMF0ucGFnZVksMCE9PXQmJih0aGlzLl92aWV3cG9ydEVsZW1lbnQuc2Nyb2xsVG9wKz10LHRoaXMuX2J1YmJsZVNjcm9sbChlLHQpKX0sbyhbcyg0LHUuSUJ1ZmZlclNlcnZpY2UpLHMoNSx1LklPcHRpb25zU2VydmljZSkscyg2LGwuSUNoYXJTaXplU2VydmljZSkscyg3LGwuSVJlbmRlclNlcnZpY2UpXSx0KX0oYS5EaXNwb3NhYmxlKTt0LlZpZXdwb3J0PWh9LDI5NTA6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Db21wb3NpdGlvbkhlbHBlcj12b2lkIDA7dmFyIG89cig0NzI1KSxzPXIoMjU4NSksYT1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSx0LHIsaSxuLG8pe3RoaXMuX3RleHRhcmVhPWUsdGhpcy5fY29tcG9zaXRpb25WaWV3PXQsdGhpcy5fYnVmZmVyU2VydmljZT1yLHRoaXMuX29wdGlvbnNTZXJ2aWNlPWksdGhpcy5fY29yZVNlcnZpY2U9bix0aGlzLl9yZW5kZXJTZXJ2aWNlPW8sdGhpcy5faXNDb21wb3Npbmc9ITEsdGhpcy5faXNTZW5kaW5nQ29tcG9zaXRpb249ITEsdGhpcy5fY29tcG9zaXRpb25Qb3NpdGlvbj17c3RhcnQ6MCxlbmQ6MH0sdGhpcy5fZGF0YUFscmVhZHlTZW50PSIifXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImlzQ29tcG9zaW5nIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2lzQ29tcG9zaW5nfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmNvbXBvc2l0aW9uc3RhcnQ9ZnVuY3Rpb24oKXt0aGlzLl9pc0NvbXBvc2luZz0hMCx0aGlzLl9jb21wb3NpdGlvblBvc2l0aW9uLnN0YXJ0PXRoaXMuX3RleHRhcmVhLnZhbHVlLmxlbmd0aCx0aGlzLl9jb21wb3NpdGlvblZpZXcudGV4dENvbnRlbnQ9IiIsdGhpcy5fZGF0YUFscmVhZHlTZW50PSIiLHRoaXMuX2NvbXBvc2l0aW9uVmlldy5jbGFzc0xpc3QuYWRkKCJhY3RpdmUiKX0sZS5wcm90b3R5cGUuY29tcG9zaXRpb251cGRhdGU9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpczt0aGlzLl9jb21wb3NpdGlvblZpZXcudGV4dENvbnRlbnQ9ZS5kYXRhLHRoaXMudXBkYXRlQ29tcG9zaXRpb25FbGVtZW50cygpLHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7dC5fY29tcG9zaXRpb25Qb3NpdGlvbi5lbmQ9dC5fdGV4dGFyZWEudmFsdWUubGVuZ3RofSksMCl9LGUucHJvdG90eXBlLmNvbXBvc2l0aW9uZW5kPWZ1bmN0aW9uKCl7dGhpcy5fZmluYWxpemVDb21wb3NpdGlvbighMCl9LGUucHJvdG90eXBlLmtleWRvd249ZnVuY3Rpb24oZSl7aWYodGhpcy5faXNDb21wb3Npbmd8fHRoaXMuX2lzU2VuZGluZ0NvbXBvc2l0aW9uKXtpZigyMjk9PT1lLmtleUNvZGUpcmV0dXJuITE7aWYoMTY9PT1lLmtleUNvZGV8fDE3PT09ZS5rZXlDb2RlfHwxOD09PWUua2V5Q29kZSlyZXR1cm4hMTt0aGlzLl9maW5hbGl6ZUNvbXBvc2l0aW9uKCExKX1yZXR1cm4gMjI5IT09ZS5rZXlDb2RlfHwodGhpcy5faGFuZGxlQW55VGV4dGFyZWFDaGFuZ2VzKCksITEpfSxlLnByb3RvdHlwZS5fZmluYWxpemVDb21wb3NpdGlvbj1mdW5jdGlvbihlKXt2YXIgdD10aGlzO2lmKHRoaXMuX2NvbXBvc2l0aW9uVmlldy5jbGFzc0xpc3QucmVtb3ZlKCJhY3RpdmUiKSx0aGlzLl9pc0NvbXBvc2luZz0hMSxlKXt2YXIgcj17c3RhcnQ6dGhpcy5fY29tcG9zaXRpb25Qb3NpdGlvbi5zdGFydCxlbmQ6dGhpcy5fY29tcG9zaXRpb25Qb3NpdGlvbi5lbmR9O3RoaXMuX2lzU2VuZGluZ0NvbXBvc2l0aW9uPSEwLHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7dmFyIGU7dC5faXNTZW5kaW5nQ29tcG9zaXRpb24mJih0Ll9pc1NlbmRpbmdDb21wb3NpdGlvbj0hMSxyLnN0YXJ0Kz10Ll9kYXRhQWxyZWFkeVNlbnQubGVuZ3RoLChlPXQuX2lzQ29tcG9zaW5nP3QuX3RleHRhcmVhLnZhbHVlLnN1YnN0cmluZyhyLnN0YXJ0LHIuZW5kKTp0Ll90ZXh0YXJlYS52YWx1ZS5zdWJzdHJpbmcoci5zdGFydCkpLmxlbmd0aD4wJiZ0Ll9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KGUsITApKX0pLDApfWVsc2V7dGhpcy5faXNTZW5kaW5nQ29tcG9zaXRpb249ITE7dmFyIGk9dGhpcy5fdGV4dGFyZWEudmFsdWUuc3Vic3RyaW5nKHRoaXMuX2NvbXBvc2l0aW9uUG9zaXRpb24uc3RhcnQsdGhpcy5fY29tcG9zaXRpb25Qb3NpdGlvbi5lbmQpO3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoaSwhMCl9fSxlLnByb3RvdHlwZS5faGFuZGxlQW55VGV4dGFyZWFDaGFuZ2VzPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcyx0PXRoaXMuX3RleHRhcmVhLnZhbHVlO3NldFRpbWVvdXQoKGZ1bmN0aW9uKCl7aWYoIWUuX2lzQ29tcG9zaW5nKXt2YXIgcj1lLl90ZXh0YXJlYS52YWx1ZS5yZXBsYWNlKHQsIiIpO3IubGVuZ3RoPjAmJihlLl9kYXRhQWxyZWFkeVNlbnQ9cixlLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHIsITApKX19KSwwKX0sZS5wcm90b3R5cGUudXBkYXRlQ29tcG9zaXRpb25FbGVtZW50cz1mdW5jdGlvbihlKXt2YXIgdD10aGlzO2lmKHRoaXMuX2lzQ29tcG9zaW5nKXtpZih0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci5pc0N1cnNvckluVmlld3BvcnQpe3ZhciByPU1hdGgubWluKHRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLngsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLTEpLGk9dGhpcy5fcmVuZGVyU2VydmljZS5kaW1lbnNpb25zLmFjdHVhbENlbGxIZWlnaHQsbj10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55KnRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0LG89cip0aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoO3RoaXMuX2NvbXBvc2l0aW9uVmlldy5zdHlsZS5sZWZ0PW8rInB4Iix0aGlzLl9jb21wb3NpdGlvblZpZXcuc3R5bGUudG9wPW4rInB4Iix0aGlzLl9jb21wb3NpdGlvblZpZXcuc3R5bGUuaGVpZ2h0PWkrInB4Iix0aGlzLl9jb21wb3NpdGlvblZpZXcuc3R5bGUubGluZUhlaWdodD1pKyJweCIsdGhpcy5fY29tcG9zaXRpb25WaWV3LnN0eWxlLmZvbnRGYW1pbHk9dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5mb250RmFtaWx5LHRoaXMuX2NvbXBvc2l0aW9uVmlldy5zdHlsZS5mb250U2l6ZT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZvbnRTaXplKyJweCI7dmFyIHM9dGhpcy5fY29tcG9zaXRpb25WaWV3LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO3RoaXMuX3RleHRhcmVhLnN0eWxlLmxlZnQ9bysicHgiLHRoaXMuX3RleHRhcmVhLnN0eWxlLnRvcD1uKyJweCIsdGhpcy5fdGV4dGFyZWEuc3R5bGUud2lkdGg9TWF0aC5tYXgocy53aWR0aCwxKSsicHgiLHRoaXMuX3RleHRhcmVhLnN0eWxlLmhlaWdodD1NYXRoLm1heChzLmhlaWdodCwxKSsicHgiLHRoaXMuX3RleHRhcmVhLnN0eWxlLmxpbmVIZWlnaHQ9cy5oZWlnaHQrInB4In1lfHxzZXRUaW1lb3V0KChmdW5jdGlvbigpe3JldHVybiB0LnVwZGF0ZUNvbXBvc2l0aW9uRWxlbWVudHMoITApfSksMCl9fSxpKFtuKDIscy5JQnVmZmVyU2VydmljZSksbigzLHMuSU9wdGlvbnNTZXJ2aWNlKSxuKDQscy5JQ29yZVNlcnZpY2UpLG4oNSxvLklSZW5kZXJTZXJ2aWNlKV0sZSl9KCk7dC5Db21wb3NpdGlvbkhlbHBlcj1hfSw5ODA2OihlLHQpPT57ZnVuY3Rpb24gcihlLHQpe3ZhciByPXQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7cmV0dXJuW2UuY2xpZW50WC1yLmxlZnQsZS5jbGllbnRZLXIudG9wXX1PYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5nZXRSYXdCeXRlQ29vcmRzPXQuZ2V0Q29vcmRzPXQuZ2V0Q29vcmRzUmVsYXRpdmVUb0VsZW1lbnQ9dm9pZCAwLHQuZ2V0Q29vcmRzUmVsYXRpdmVUb0VsZW1lbnQ9cix0LmdldENvb3Jkcz1mdW5jdGlvbihlLHQsaSxuLG8scyxhLGMpe2lmKG8pe3ZhciBsPXIoZSx0KTtpZihsKXJldHVybiBsWzBdPU1hdGguY2VpbCgobFswXSsoYz9zLzI6MCkpL3MpLGxbMV09TWF0aC5jZWlsKGxbMV0vYSksbFswXT1NYXRoLm1pbihNYXRoLm1heChsWzBdLDEpLGkrKGM/MTowKSksbFsxXT1NYXRoLm1pbihNYXRoLm1heChsWzFdLDEpLG4pLGx9fSx0LmdldFJhd0J5dGVDb29yZHM9ZnVuY3Rpb24oZSl7aWYoZSlyZXR1cm57eDplWzBdKzMyLHk6ZVsxXSszMn19fSw5NTA0OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5tb3ZlVG9DZWxsU2VxdWVuY2U9dm9pZCAwO3ZhciBpPXIoMjU4NCk7ZnVuY3Rpb24gbihlLHQscixpKXt2YXIgbj1lLW8ocixlKSxhPXQtbyhyLHQpLHU9TWF0aC5hYnMobi1hKS1mdW5jdGlvbihlLHQscil7Zm9yKHZhciBpPTAsbj1lLW8ocixlKSxhPXQtbyhyLHQpLGM9MDtjPE1hdGguYWJzKG4tYSk7YysrKXt2YXIgbD0iQSI9PT1zKGUsdCk/LTE6MSx1PXIuYnVmZmVyLmxpbmVzLmdldChuK2wqYyk7KG51bGw9PXU/dm9pZCAwOnUuaXNXcmFwcGVkKSYmaSsrfXJldHVybiBpfShlLHQscik7cmV0dXJuIGwodSxjKHMoZSx0KSxpKSl9ZnVuY3Rpb24gbyhlLHQpe2Zvcih2YXIgcj0wLGk9ZS5idWZmZXIubGluZXMuZ2V0KHQpLG49bnVsbD09aT92b2lkIDA6aS5pc1dyYXBwZWQ7biYmdD49MCYmdDxlLnJvd3M7KXIrKyxuPW51bGw9PShpPWUuYnVmZmVyLmxpbmVzLmdldCgtLXQpKT92b2lkIDA6aS5pc1dyYXBwZWQ7cmV0dXJuIHJ9ZnVuY3Rpb24gcyhlLHQpe3JldHVybiBlPnQ/IkEiOiJCIn1mdW5jdGlvbiBhKGUsdCxyLGksbixvKXtmb3IodmFyIHM9ZSxhPXQsYz0iIjtzIT09cnx8YSE9PWk7KXMrPW4/MTotMSxuJiZzPm8uY29scy0xPyhjKz1vLmJ1ZmZlci50cmFuc2xhdGVCdWZmZXJMaW5lVG9TdHJpbmcoYSwhMSxlLHMpLHM9MCxlPTAsYSsrKTohbiYmczwwJiYoYys9by5idWZmZXIudHJhbnNsYXRlQnVmZmVyTGluZVRvU3RyaW5nKGEsITEsMCxlKzEpLGU9cz1vLmNvbHMtMSxhLS0pO3JldHVybiBjK28uYnVmZmVyLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhhLCExLGUscyl9ZnVuY3Rpb24gYyhlLHQpe3ZhciByPXQ/Ik8iOiJbIjtyZXR1cm4gaS5DMC5FU0MrcitlfWZ1bmN0aW9uIGwoZSx0KXtlPU1hdGguZmxvb3IoZSk7Zm9yKHZhciByPSIiLGk9MDtpPGU7aSsrKXIrPXQ7cmV0dXJuIHJ9dC5tb3ZlVG9DZWxsU2VxdWVuY2U9ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIHMsdT1yLmJ1ZmZlci54LGg9ci5idWZmZXIueTtpZighci5idWZmZXIuaGFzU2Nyb2xsYmFjaylyZXR1cm4gZnVuY3Rpb24oZSx0LHIsaSxzLHUpe3JldHVybiAwPT09bih0LGkscyx1KS5sZW5ndGg/IiI6bChhKGUsdCxlLHQtbyhzLHQpLCExLHMpLmxlbmd0aCxjKCJEIix1KSl9KHUsaCwwLHQscixpKStuKGgsdCxyLGkpK2Z1bmN0aW9uKGUsdCxyLGkscyx1KXt2YXIgaDtoPW4odCxpLHMsdSkubGVuZ3RoPjA/aS1vKHMsaSk6dDt2YXIgZj1pLF89ZnVuY3Rpb24oZSx0LHIsaSxzLGEpe3ZhciBjO3JldHVybiBjPW4ocixpLHMsYSkubGVuZ3RoPjA/aS1vKHMsaSk6dCxlPHImJmM8PWl8fGU+PXImJmM8aT8iQyI6IkQifShlLHQscixpLHMsdSk7cmV0dXJuIGwoYShlLGgscixmLCJDIj09PV8scykubGVuZ3RoLGMoXyx1KSl9KHUsaCxlLHQscixpKTtpZihoPT09dClyZXR1cm4gcz11PmU/IkQiOiJDIixsKE1hdGguYWJzKHUtZSksYyhzLGkpKTtzPWg+dD8iRCI6IkMiO3ZhciBmPU1hdGguYWJzKGgtdCk7cmV0dXJuIGwoZnVuY3Rpb24oZSx0KXtyZXR1cm4gdC5jb2xzLWV9KGg+dD9lOnUscikrKGYtMSkqci5jb2xzKzErKChoPnQ/dTplKS0xKSxjKHMsaSkpfX0sMTU0NjooZSx0LHIpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQmFzZVJlbmRlckxheWVyPXZvaWQgMDt2YXIgaT1yKDY0Myksbj1yKDg4MDMpLG89cigxNDIwKSxzPXIoMzczNCksYT1yKDE3NTIpLGM9cig0Nzc0KSxsPXIoOTYzMSksdT1yKDg5NzgpLGg9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyLGksbixvLHMsYSl7dGhpcy5fY29udGFpbmVyPWUsdGhpcy5fYWxwaGE9aSx0aGlzLl9jb2xvcnM9bix0aGlzLl9yZW5kZXJlcklkPW8sdGhpcy5fYnVmZmVyU2VydmljZT1zLHRoaXMuX29wdGlvbnNTZXJ2aWNlPWEsdGhpcy5fc2NhbGVkQ2hhcldpZHRoPTAsdGhpcy5fc2NhbGVkQ2hhckhlaWdodD0wLHRoaXMuX3NjYWxlZENlbGxXaWR0aD0wLHRoaXMuX3NjYWxlZENlbGxIZWlnaHQ9MCx0aGlzLl9zY2FsZWRDaGFyTGVmdD0wLHRoaXMuX3NjYWxlZENoYXJUb3A9MCx0aGlzLl9jdXJyZW50R2x5cGhJZGVudGlmaWVyPXtjaGFyczoiIixjb2RlOjAsYmc6MCxmZzowLGJvbGQ6ITEsZGltOiExLGl0YWxpYzohMX0sdGhpcy5fY2FudmFzPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImNhbnZhcyIpLHRoaXMuX2NhbnZhcy5jbGFzc0xpc3QuYWRkKCJ4dGVybS0iK3QrIi1sYXllciIpLHRoaXMuX2NhbnZhcy5zdHlsZS56SW5kZXg9ci50b1N0cmluZygpLHRoaXMuX2luaXRDYW52YXMoKSx0aGlzLl9jb250YWluZXIuYXBwZW5kQ2hpbGQodGhpcy5fY2FudmFzKX1yZXR1cm4gZS5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3ZhciBlOygwLGwucmVtb3ZlRWxlbWVudEZyb21QYXJlbnQpKHRoaXMuX2NhbnZhcyksbnVsbD09PShlPXRoaXMuX2NoYXJBdGxhcyl8fHZvaWQgMD09PWV8fGUuZGlzcG9zZSgpfSxlLnByb3RvdHlwZS5faW5pdENhbnZhcz1mdW5jdGlvbigpe3RoaXMuX2N0eD0oMCxhLnRocm93SWZGYWxzeSkodGhpcy5fY2FudmFzLmdldENvbnRleHQoIjJkIix7YWxwaGE6dGhpcy5fYWxwaGF9KSksdGhpcy5fYWxwaGF8fHRoaXMuX2NsZWFyQWxsKCl9LGUucHJvdG90eXBlLm9uT3B0aW9uc0NoYW5nZWQ9ZnVuY3Rpb24oKXt9LGUucHJvdG90eXBlLm9uQmx1cj1mdW5jdGlvbigpe30sZS5wcm90b3R5cGUub25Gb2N1cz1mdW5jdGlvbigpe30sZS5wcm90b3R5cGUub25DdXJzb3JNb3ZlPWZ1bmN0aW9uKCl7fSxlLnByb3RvdHlwZS5vbkdyaWRDaGFuZ2VkPWZ1bmN0aW9uKGUsdCl7fSxlLnByb3RvdHlwZS5vblNlbGVjdGlvbkNoYW5nZWQ9ZnVuY3Rpb24oZSx0LHIpe3ZvaWQgMD09PXImJihyPSExKX0sZS5wcm90b3R5cGUuc2V0Q29sb3JzPWZ1bmN0aW9uKGUpe3RoaXMuX3JlZnJlc2hDaGFyQXRsYXMoZSl9LGUucHJvdG90eXBlLl9zZXRUcmFuc3BhcmVuY3k9ZnVuY3Rpb24oZSl7aWYoZSE9PXRoaXMuX2FscGhhKXt2YXIgdD10aGlzLl9jYW52YXM7dGhpcy5fYWxwaGE9ZSx0aGlzLl9jYW52YXM9dGhpcy5fY2FudmFzLmNsb25lTm9kZSgpLHRoaXMuX2luaXRDYW52YXMoKSx0aGlzLl9jb250YWluZXIucmVwbGFjZUNoaWxkKHRoaXMuX2NhbnZhcyx0KSx0aGlzLl9yZWZyZXNoQ2hhckF0bGFzKHRoaXMuX2NvbG9ycyksdGhpcy5vbkdyaWRDaGFuZ2VkKDAsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEpfX0sZS5wcm90b3R5cGUuX3JlZnJlc2hDaGFyQXRsYXM9ZnVuY3Rpb24oZSl7dGhpcy5fc2NhbGVkQ2hhcldpZHRoPD0wJiZ0aGlzLl9zY2FsZWRDaGFySGVpZ2h0PD0wfHwodGhpcy5fY2hhckF0bGFzPSgwLG8uYWNxdWlyZUNoYXJBdGxhcykodGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucyx0aGlzLl9yZW5kZXJlcklkLGUsdGhpcy5fc2NhbGVkQ2hhcldpZHRoLHRoaXMuX3NjYWxlZENoYXJIZWlnaHQpLHRoaXMuX2NoYXJBdGxhcy53YXJtVXAoKSl9LGUucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlKXt0aGlzLl9zY2FsZWRDZWxsV2lkdGg9ZS5zY2FsZWRDZWxsV2lkdGgsdGhpcy5fc2NhbGVkQ2VsbEhlaWdodD1lLnNjYWxlZENlbGxIZWlnaHQsdGhpcy5fc2NhbGVkQ2hhcldpZHRoPWUuc2NhbGVkQ2hhcldpZHRoLHRoaXMuX3NjYWxlZENoYXJIZWlnaHQ9ZS5zY2FsZWRDaGFySGVpZ2h0LHRoaXMuX3NjYWxlZENoYXJMZWZ0PWUuc2NhbGVkQ2hhckxlZnQsdGhpcy5fc2NhbGVkQ2hhclRvcD1lLnNjYWxlZENoYXJUb3AsdGhpcy5fY2FudmFzLndpZHRoPWUuc2NhbGVkQ2FudmFzV2lkdGgsdGhpcy5fY2FudmFzLmhlaWdodD1lLnNjYWxlZENhbnZhc0hlaWdodCx0aGlzLl9jYW52YXMuc3R5bGUud2lkdGg9ZS5jYW52YXNXaWR0aCsicHgiLHRoaXMuX2NhbnZhcy5zdHlsZS5oZWlnaHQ9ZS5jYW52YXNIZWlnaHQrInB4Iix0aGlzLl9hbHBoYXx8dGhpcy5fY2xlYXJBbGwoKSx0aGlzLl9yZWZyZXNoQ2hhckF0bGFzKHRoaXMuX2NvbG9ycyl9LGUucHJvdG90eXBlLmNsZWFyVGV4dHVyZUF0bGFzPWZ1bmN0aW9uKCl7dmFyIGU7bnVsbD09PShlPXRoaXMuX2NoYXJBdGxhcyl8fHZvaWQgMD09PWV8fGUuY2xlYXIoKX0sZS5wcm90b3R5cGUuX2ZpbGxDZWxscz1mdW5jdGlvbihlLHQscixpKXt0aGlzLl9jdHguZmlsbFJlY3QoZSp0aGlzLl9zY2FsZWRDZWxsV2lkdGgsdCp0aGlzLl9zY2FsZWRDZWxsSGVpZ2h0LHIqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLGkqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCl9LGUucHJvdG90eXBlLl9maWxsTWlkZGxlTGluZUF0Q2VsbHM9ZnVuY3Rpb24oZSx0LHIpe3ZvaWQgMD09PXImJihyPTEpO3ZhciBpPU1hdGguY2VpbCguNSp0aGlzLl9zY2FsZWRDZWxsSGVpZ2h0KTt0aGlzLl9jdHguZmlsbFJlY3QoZSp0aGlzLl9zY2FsZWRDZWxsV2lkdGgsKHQrMSkqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodC1pLXdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHIqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKX0sZS5wcm90b3R5cGUuX2ZpbGxCb3R0b21MaW5lQXRDZWxscz1mdW5jdGlvbihlLHQscil7dm9pZCAwPT09ciYmKHI9MSksdGhpcy5fY3R4LmZpbGxSZWN0KGUqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLCh0KzEpKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQtd2luZG93LmRldmljZVBpeGVsUmF0aW8tMSxyKnRoaXMuX3NjYWxlZENlbGxXaWR0aCx3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyl9LGUucHJvdG90eXBlLl9maWxsTGVmdExpbmVBdENlbGw9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2N0eC5maWxsUmVjdChlKnRoaXMuX3NjYWxlZENlbGxXaWR0aCx0KnRoaXMuX3NjYWxlZENlbGxIZWlnaHQsd2luZG93LmRldmljZVBpeGVsUmF0aW8qcix0aGlzLl9zY2FsZWRDZWxsSGVpZ2h0KX0sZS5wcm90b3R5cGUuX3N0cm9rZVJlY3RBdENlbGw9ZnVuY3Rpb24oZSx0LHIsaSl7dGhpcy5fY3R4LmxpbmVXaWR0aD13aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyx0aGlzLl9jdHguc3Ryb2tlUmVjdChlKnRoaXMuX3NjYWxlZENlbGxXaWR0aCt3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpby8yLHQqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCt3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpby8yLHIqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLXdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLGkqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodC13aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyl9LGUucHJvdG90eXBlLl9jbGVhckFsbD1mdW5jdGlvbigpe3RoaXMuX2FscGhhP3RoaXMuX2N0eC5jbGVhclJlY3QoMCwwLHRoaXMuX2NhbnZhcy53aWR0aCx0aGlzLl9jYW52YXMuaGVpZ2h0KToodGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuYmFja2dyb3VuZC5jc3MsdGhpcy5fY3R4LmZpbGxSZWN0KDAsMCx0aGlzLl9jYW52YXMud2lkdGgsdGhpcy5fY2FudmFzLmhlaWdodCkpfSxlLnByb3RvdHlwZS5fY2xlYXJDZWxscz1mdW5jdGlvbihlLHQscixpKXt0aGlzLl9hbHBoYT90aGlzLl9jdHguY2xlYXJSZWN0KGUqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHQqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCxyKnRoaXMuX3NjYWxlZENlbGxXaWR0aCxpKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQpOih0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5iYWNrZ3JvdW5kLmNzcyx0aGlzLl9jdHguZmlsbFJlY3QoZSp0aGlzLl9zY2FsZWRDZWxsV2lkdGgsdCp0aGlzLl9zY2FsZWRDZWxsSGVpZ2h0LHIqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLGkqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCkpfSxlLnByb3RvdHlwZS5fZmlsbENoYXJUcnVlQ29sb3I9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2N0eC5mb250PXRoaXMuX2dldEZvbnQoITEsITEpLHRoaXMuX2N0eC50ZXh0QmFzZWxpbmU9bi5URVhUX0JBU0VMSU5FLHRoaXMuX2NsaXBSb3cocik7dmFyIGk9ITE7ITEhPT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmN1c3RvbUdseXBocyYmKGk9KDAsdS50cnlEcmF3Q3VzdG9tQ2hhcikodGhpcy5fY3R4LGUuZ2V0Q2hhcnMoKSx0KnRoaXMuX3NjYWxlZENlbGxXaWR0aCxyKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQsdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHRoaXMuX3NjYWxlZENlbGxIZWlnaHQpKSxpfHx0aGlzLl9jdHguZmlsbFRleHQoZS5nZXRDaGFycygpLHQqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoK3RoaXMuX3NjYWxlZENoYXJMZWZ0LHIqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCt0aGlzLl9zY2FsZWRDaGFyVG9wK3RoaXMuX3NjYWxlZENoYXJIZWlnaHQpfSxlLnByb3RvdHlwZS5fZHJhd0NoYXJzPWZ1bmN0aW9uKGUsdCxyKXt2YXIgbyxzLGEsYz10aGlzLl9nZXRDb250cmFzdENvbG9yKGUpO2N8fGUuaXNGZ1JHQigpfHxlLmlzQmdSR0IoKT90aGlzLl9kcmF3VW5jYWNoZWRDaGFycyhlLHQscixjKTooZS5pc0ludmVyc2UoKT8ocz1lLmlzQmdEZWZhdWx0KCk/bi5JTlZFUlRFRF9ERUZBVUxUX0NPTE9SOmUuZ2V0QmdDb2xvcigpLGE9ZS5pc0ZnRGVmYXVsdCgpP24uSU5WRVJURURfREVGQVVMVF9DT0xPUjplLmdldEZnQ29sb3IoKSk6KGE9ZS5pc0JnRGVmYXVsdCgpP2kuREVGQVVMVF9DT0xPUjplLmdldEJnQ29sb3IoKSxzPWUuaXNGZ0RlZmF1bHQoKT9pLkRFRkFVTFRfQ09MT1I6ZS5nZXRGZ0NvbG9yKCkpLHMrPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZHJhd0JvbGRUZXh0SW5CcmlnaHRDb2xvcnMmJmUuaXNCb2xkKCkmJnM8OD84OjAsdGhpcy5fY3VycmVudEdseXBoSWRlbnRpZmllci5jaGFycz1lLmdldENoYXJzKCl8fGkuV0hJVEVTUEFDRV9DRUxMX0NIQVIsdGhpcy5fY3VycmVudEdseXBoSWRlbnRpZmllci5jb2RlPWUuZ2V0Q29kZSgpfHxpLldISVRFU1BBQ0VfQ0VMTF9DT0RFLHRoaXMuX2N1cnJlbnRHbHlwaElkZW50aWZpZXIuYmc9YSx0aGlzLl9jdXJyZW50R2x5cGhJZGVudGlmaWVyLmZnPXMsdGhpcy5fY3VycmVudEdseXBoSWRlbnRpZmllci5ib2xkPSEhZS5pc0JvbGQoKSx0aGlzLl9jdXJyZW50R2x5cGhJZGVudGlmaWVyLmRpbT0hIWUuaXNEaW0oKSx0aGlzLl9jdXJyZW50R2x5cGhJZGVudGlmaWVyLml0YWxpYz0hIWUuaXNJdGFsaWMoKSwobnVsbD09PShvPXRoaXMuX2NoYXJBdGxhcyl8fHZvaWQgMD09PW8/dm9pZCAwOm8uZHJhdyh0aGlzLl9jdHgsdGhpcy5fY3VycmVudEdseXBoSWRlbnRpZmllcix0KnRoaXMuX3NjYWxlZENlbGxXaWR0aCt0aGlzLl9zY2FsZWRDaGFyTGVmdCxyKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQrdGhpcy5fc2NhbGVkQ2hhclRvcCkpfHx0aGlzLl9kcmF3VW5jYWNoZWRDaGFycyhlLHQscikpfSxlLnByb3RvdHlwZS5fZHJhd1VuY2FjaGVkQ2hhcnM9ZnVuY3Rpb24oZSx0LHIsaSl7aWYodGhpcy5fY3R4LnNhdmUoKSx0aGlzLl9jdHguZm9udD10aGlzLl9nZXRGb250KCEhZS5pc0JvbGQoKSwhIWUuaXNJdGFsaWMoKSksdGhpcy5fY3R4LnRleHRCYXNlbGluZT1uLlRFWFRfQkFTRUxJTkUsZS5pc0ludmVyc2UoKSlpZihpKXRoaXMuX2N0eC5maWxsU3R5bGU9aS5jc3M7ZWxzZSBpZihlLmlzQmdEZWZhdWx0KCkpdGhpcy5fY3R4LmZpbGxTdHlsZT1jLmNvbG9yLm9wYXF1ZSh0aGlzLl9jb2xvcnMuYmFja2dyb3VuZCkuY3NzO2Vsc2UgaWYoZS5pc0JnUkdCKCkpdGhpcy5fY3R4LmZpbGxTdHlsZT0icmdiKCIrcy5BdHRyaWJ1dGVEYXRhLnRvQ29sb3JSR0IoZS5nZXRCZ0NvbG9yKCkpLmpvaW4oIiwiKSsiKSI7ZWxzZXt2YXIgbz1lLmdldEJnQ29sb3IoKTt0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmRyYXdCb2xkVGV4dEluQnJpZ2h0Q29sb3JzJiZlLmlzQm9sZCgpJiZvPDgmJihvKz04KSx0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5hbnNpW29dLmNzc31lbHNlIGlmKGkpdGhpcy5fY3R4LmZpbGxTdHlsZT1pLmNzcztlbHNlIGlmKGUuaXNGZ0RlZmF1bHQoKSl0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5mb3JlZ3JvdW5kLmNzcztlbHNlIGlmKGUuaXNGZ1JHQigpKXRoaXMuX2N0eC5maWxsU3R5bGU9InJnYigiK3MuQXR0cmlidXRlRGF0YS50b0NvbG9yUkdCKGUuZ2V0RmdDb2xvcigpKS5qb2luKCIsIikrIikiO2Vsc2V7dmFyIGE9ZS5nZXRGZ0NvbG9yKCk7dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5kcmF3Qm9sZFRleHRJbkJyaWdodENvbG9ycyYmZS5pc0JvbGQoKSYmYTw4JiYoYSs9OCksdGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuYW5zaVthXS5jc3N9dGhpcy5fY2xpcFJvdyhyKSxlLmlzRGltKCkmJih0aGlzLl9jdHguZ2xvYmFsQWxwaGE9bi5ESU1fT1BBQ0lUWSk7dmFyIGw9ITE7ITEhPT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmN1c3RvbUdseXBocyYmKGw9KDAsdS50cnlEcmF3Q3VzdG9tQ2hhcikodGhpcy5fY3R4LGUuZ2V0Q2hhcnMoKSx0KnRoaXMuX3NjYWxlZENlbGxXaWR0aCxyKnRoaXMuX3NjYWxlZENlbGxIZWlnaHQsdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHRoaXMuX3NjYWxlZENlbGxIZWlnaHQpKSxsfHx0aGlzLl9jdHguZmlsbFRleHQoZS5nZXRDaGFycygpLHQqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoK3RoaXMuX3NjYWxlZENoYXJMZWZ0LHIqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCt0aGlzLl9zY2FsZWRDaGFyVG9wK3RoaXMuX3NjYWxlZENoYXJIZWlnaHQpLHRoaXMuX2N0eC5yZXN0b3JlKCl9LGUucHJvdG90eXBlLl9jbGlwUm93PWZ1bmN0aW9uKGUpe3RoaXMuX2N0eC5iZWdpblBhdGgoKSx0aGlzLl9jdHgucmVjdCgwLGUqdGhpcy5fc2NhbGVkQ2VsbEhlaWdodCx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMqdGhpcy5fc2NhbGVkQ2VsbFdpZHRoLHRoaXMuX3NjYWxlZENlbGxIZWlnaHQpLHRoaXMuX2N0eC5jbGlwKCl9LGUucHJvdG90eXBlLl9nZXRGb250PWZ1bmN0aW9uKGUsdCl7cmV0dXJuKHQ/Iml0YWxpYyI6IiIpKyIgIisoZT90aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZvbnRXZWlnaHRCb2xkOnRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZm9udFdlaWdodCkrIiAiK3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZm9udFNpemUqd2luZG93LmRldmljZVBpeGVsUmF0aW8rInB4ICIrdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5mb250RmFtaWx5fSxlLnByb3RvdHlwZS5fZ2V0Q29udHJhc3RDb2xvcj1mdW5jdGlvbihlKXtpZigxIT09dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5taW5pbXVtQ29udHJhc3RSYXRpbyl7dmFyIHQ9dGhpcy5fY29sb3JzLmNvbnRyYXN0Q2FjaGUuZ2V0Q29sb3IoZS5iZyxlLmZnKTtpZih2b2lkIDAhPT10KXJldHVybiB0fHx2b2lkIDA7dmFyIHI9ZS5nZXRGZ0NvbG9yKCksaT1lLmdldEZnQ29sb3JNb2RlKCksbj1lLmdldEJnQ29sb3IoKSxvPWUuZ2V0QmdDb2xvck1vZGUoKSxzPSEhZS5pc0ludmVyc2UoKSxhPSEhZS5pc0ludmVyc2UoKTtpZihzKXt2YXIgbD1yO3I9bixuPWw7dmFyIHU9aTtpPW8sbz11fXZhciBoPXRoaXMuX3Jlc29sdmVCYWNrZ3JvdW5kUmdiYShvLG4scyksZj10aGlzLl9yZXNvbHZlRm9yZWdyb3VuZFJnYmEoaSxyLHMsYSksXz1jLnJnYmEuZW5zdXJlQ29udHJhc3RSYXRpbyhoLGYsdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5taW5pbXVtQ29udHJhc3RSYXRpbyk7aWYoXyl7dmFyIGQ9e2NzczpjLmNoYW5uZWxzLnRvQ3NzKF8+PjI0JjI1NSxfPj4xNiYyNTUsXz4+OCYyNTUpLHJnYmE6X307cmV0dXJuIHRoaXMuX2NvbG9ycy5jb250cmFzdENhY2hlLnNldENvbG9yKGUuYmcsZS5mZyxkKSxkfXRoaXMuX2NvbG9ycy5jb250cmFzdENhY2hlLnNldENvbG9yKGUuYmcsZS5mZyxudWxsKX19LGUucHJvdG90eXBlLl9yZXNvbHZlQmFja2dyb3VuZFJnYmE9ZnVuY3Rpb24oZSx0LHIpe3N3aXRjaChlKXtjYXNlIDE2Nzc3MjE2OmNhc2UgMzM1NTQ0MzI6cmV0dXJuIHRoaXMuX2NvbG9ycy5hbnNpW3RdLnJnYmE7Y2FzZSA1MDMzMTY0ODpyZXR1cm4gdDw8ODtkZWZhdWx0OnJldHVybiByP3RoaXMuX2NvbG9ycy5mb3JlZ3JvdW5kLnJnYmE6dGhpcy5fY29sb3JzLmJhY2tncm91bmQucmdiYX19LGUucHJvdG90eXBlLl9yZXNvbHZlRm9yZWdyb3VuZFJnYmE9ZnVuY3Rpb24oZSx0LHIsaSl7c3dpdGNoKGUpe2Nhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpyZXR1cm4gdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5kcmF3Qm9sZFRleHRJbkJyaWdodENvbG9ycyYmaSYmdDw4JiYodCs9OCksdGhpcy5fY29sb3JzLmFuc2lbdF0ucmdiYTtjYXNlIDUwMzMxNjQ4OnJldHVybiB0PDw4O2RlZmF1bHQ6cmV0dXJuIHI/dGhpcy5fY29sb3JzLmJhY2tncm91bmQucmdiYTp0aGlzLl9jb2xvcnMuZm9yZWdyb3VuZC5yZ2JhfX0sZX0oKTt0LkJhc2VSZW5kZXJMYXllcj1ofSwyNTEyOmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkN1cnNvclJlbmRlckxheWVyPXZvaWQgMDt2YXIgYT1yKDE1NDYpLGM9cig1MTEpLGw9cigyNTg1KSx1PXIoNDcyNSksaD02MDAsZj1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpLG4sbyxzLGEsbCx1KXt2YXIgaD1lLmNhbGwodGhpcyx0LCJjdXJzb3IiLHIsITAsaSxuLHMsYSl8fHRoaXM7cmV0dXJuIGguX29uUmVxdWVzdFJlZHJhdz1vLGguX2NvcmVTZXJ2aWNlPWwsaC5fY29yZUJyb3dzZXJTZXJ2aWNlPXUsaC5fY2VsbD1uZXcgYy5DZWxsRGF0YSxoLl9zdGF0ZT17eDowLHk6MCxpc0ZvY3VzZWQ6ITEsc3R5bGU6IiIsd2lkdGg6MH0saC5fY3Vyc29yUmVuZGVyZXJzPXtiYXI6aC5fcmVuZGVyQmFyQ3Vyc29yLmJpbmQoaCksYmxvY2s6aC5fcmVuZGVyQmxvY2tDdXJzb3IuYmluZChoKSx1bmRlcmxpbmU6aC5fcmVuZGVyVW5kZXJsaW5lQ3Vyc29yLmJpbmQoaCl9LGh9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXImJih0aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlci5kaXNwb3NlKCksdGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXI9dm9pZCAwKSxlLnByb3RvdHlwZS5kaXNwb3NlLmNhbGwodGhpcyl9LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbih0KXtlLnByb3RvdHlwZS5yZXNpemUuY2FsbCh0aGlzLHQpLHRoaXMuX3N0YXRlPXt4OjAseTowLGlzRm9jdXNlZDohMSxzdHlsZToiIix3aWR0aDowfX0sdC5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt2YXIgZTt0aGlzLl9jbGVhckN1cnNvcigpLG51bGw9PT0oZT10aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcil8fHZvaWQgMD09PWV8fGUucmVzdGFydEJsaW5rQW5pbWF0aW9uKCksdGhpcy5vbk9wdGlvbnNDaGFuZ2VkKCl9LHQucHJvdG90eXBlLm9uQmx1cj1mdW5jdGlvbigpe3ZhciBlO251bGw9PT0oZT10aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcil8fHZvaWQgMD09PWV8fGUucGF1c2UoKSx0aGlzLl9vblJlcXVlc3RSZWRyYXcuZmlyZSh7c3RhcnQ6dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueSxlbmQ6dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueX0pfSx0LnByb3RvdHlwZS5vbkZvY3VzPWZ1bmN0aW9uKCl7dmFyIGU7bnVsbD09PShlPXRoaXMuX2N1cnNvckJsaW5rU3RhdGVNYW5hZ2VyKXx8dm9pZCAwPT09ZXx8ZS5yZXN1bWUoKSx0aGlzLl9vblJlcXVlc3RSZWRyYXcuZmlyZSh7c3RhcnQ6dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueSxlbmQ6dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueX0pfSx0LnByb3RvdHlwZS5vbk9wdGlvbnNDaGFuZ2VkPWZ1bmN0aW9uKCl7dmFyIGUsdD10aGlzO3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yQmxpbms/dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXJ8fCh0aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcj1uZXcgXyh0aGlzLl9jb3JlQnJvd3NlclNlcnZpY2UuaXNGb2N1c2VkLChmdW5jdGlvbigpe3QuX3JlbmRlcighMCl9KSkpOihudWxsPT09KGU9dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXIpfHx2b2lkIDA9PT1lfHxlLmRpc3Bvc2UoKSx0aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcj12b2lkIDApLHRoaXMuX29uUmVxdWVzdFJlZHJhdy5maXJlKHtzdGFydDp0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55LGVuZDp0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55fSl9LHQucHJvdG90eXBlLm9uQ3Vyc29yTW92ZT1mdW5jdGlvbigpe3ZhciBlO251bGw9PT0oZT10aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcil8fHZvaWQgMD09PWV8fGUucmVzdGFydEJsaW5rQW5pbWF0aW9uKCl9LHQucHJvdG90eXBlLm9uR3JpZENoYW5nZWQ9ZnVuY3Rpb24oZSx0KXshdGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXJ8fHRoaXMuX2N1cnNvckJsaW5rU3RhdGVNYW5hZ2VyLmlzUGF1c2VkP3RoaXMuX3JlbmRlcighMSk6dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXIucmVzdGFydEJsaW5rQW5pbWF0aW9uKCl9LHQucHJvdG90eXBlLl9yZW5kZXI9ZnVuY3Rpb24oZSl7aWYodGhpcy5fY29yZVNlcnZpY2UuaXNDdXJzb3JJbml0aWFsaXplZCYmIXRoaXMuX2NvcmVTZXJ2aWNlLmlzQ3Vyc29ySGlkZGVuKXt2YXIgdD10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55YmFzZSt0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55LHI9dC10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcDtpZihyPDB8fHI+PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyl0aGlzLl9jbGVhckN1cnNvcigpO2Vsc2V7dmFyIGk9TWF0aC5taW4odGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueCx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtMSk7aWYodGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KHQpLmxvYWRDZWxsKGksdGhpcy5fY2VsbCksdm9pZCAwIT09dGhpcy5fY2VsbC5jb250ZW50KXtpZighdGhpcy5fY29yZUJyb3dzZXJTZXJ2aWNlLmlzRm9jdXNlZCl7dGhpcy5fY2xlYXJDdXJzb3IoKSx0aGlzLl9jdHguc2F2ZSgpLHRoaXMuX2N0eC5maWxsU3R5bGU9dGhpcy5fY29sb3JzLmN1cnNvci5jc3M7dmFyIG49dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JTdHlsZTtyZXR1cm4gbiYmImJsb2NrIiE9PW4/dGhpcy5fY3Vyc29yUmVuZGVyZXJzW25dKGkscix0aGlzLl9jZWxsKTp0aGlzLl9yZW5kZXJCbHVyQ3Vyc29yKGkscix0aGlzLl9jZWxsKSx0aGlzLl9jdHgucmVzdG9yZSgpLHRoaXMuX3N0YXRlLng9aSx0aGlzLl9zdGF0ZS55PXIsdGhpcy5fc3RhdGUuaXNGb2N1c2VkPSExLHRoaXMuX3N0YXRlLnN0eWxlPW4sdm9pZCh0aGlzLl9zdGF0ZS53aWR0aD10aGlzLl9jZWxsLmdldFdpZHRoKCkpfWlmKCF0aGlzLl9jdXJzb3JCbGlua1N0YXRlTWFuYWdlcnx8dGhpcy5fY3Vyc29yQmxpbmtTdGF0ZU1hbmFnZXIuaXNDdXJzb3JWaXNpYmxlKXtpZih0aGlzLl9zdGF0ZSl7aWYodGhpcy5fc3RhdGUueD09PWkmJnRoaXMuX3N0YXRlLnk9PT1yJiZ0aGlzLl9zdGF0ZS5pc0ZvY3VzZWQ9PT10aGlzLl9jb3JlQnJvd3NlclNlcnZpY2UuaXNGb2N1c2VkJiZ0aGlzLl9zdGF0ZS5zdHlsZT09PXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGUmJnRoaXMuX3N0YXRlLndpZHRoPT09dGhpcy5fY2VsbC5nZXRXaWR0aCgpKXJldHVybjt0aGlzLl9jbGVhckN1cnNvcigpfXRoaXMuX2N0eC5zYXZlKCksdGhpcy5fY3Vyc29yUmVuZGVyZXJzW3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGV8fCJibG9jayJdKGkscix0aGlzLl9jZWxsKSx0aGlzLl9jdHgucmVzdG9yZSgpLHRoaXMuX3N0YXRlLng9aSx0aGlzLl9zdGF0ZS55PXIsdGhpcy5fc3RhdGUuaXNGb2N1c2VkPSExLHRoaXMuX3N0YXRlLnN0eWxlPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGUsdGhpcy5fc3RhdGUud2lkdGg9dGhpcy5fY2VsbC5nZXRXaWR0aCgpfWVsc2UgdGhpcy5fY2xlYXJDdXJzb3IoKX19fWVsc2UgdGhpcy5fY2xlYXJDdXJzb3IoKX0sdC5wcm90b3R5cGUuX2NsZWFyQ3Vyc29yPWZ1bmN0aW9uKCl7dGhpcy5fc3RhdGUmJih3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbzwxP3RoaXMuX2NsZWFyQWxsKCk6dGhpcy5fY2xlYXJDZWxscyh0aGlzLl9zdGF0ZS54LHRoaXMuX3N0YXRlLnksdGhpcy5fc3RhdGUud2lkdGgsMSksdGhpcy5fc3RhdGU9e3g6MCx5OjAsaXNGb2N1c2VkOiExLHN0eWxlOiIiLHdpZHRoOjB9KX0sdC5wcm90b3R5cGUuX3JlbmRlckJhckN1cnNvcj1mdW5jdGlvbihlLHQscil7dGhpcy5fY3R4LnNhdmUoKSx0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5jdXJzb3IuY3NzLHRoaXMuX2ZpbGxMZWZ0TGluZUF0Q2VsbChlLHQsdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JXaWR0aCksdGhpcy5fY3R4LnJlc3RvcmUoKX0sdC5wcm90b3R5cGUuX3JlbmRlckJsb2NrQ3Vyc29yPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9jdHguc2F2ZSgpLHRoaXMuX2N0eC5maWxsU3R5bGU9dGhpcy5fY29sb3JzLmN1cnNvci5jc3MsdGhpcy5fZmlsbENlbGxzKGUsdCxyLmdldFdpZHRoKCksMSksdGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuY3Vyc29yQWNjZW50LmNzcyx0aGlzLl9maWxsQ2hhclRydWVDb2xvcihyLGUsdCksdGhpcy5fY3R4LnJlc3RvcmUoKX0sdC5wcm90b3R5cGUuX3JlbmRlclVuZGVybGluZUN1cnNvcj1mdW5jdGlvbihlLHQscil7dGhpcy5fY3R4LnNhdmUoKSx0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5jdXJzb3IuY3NzLHRoaXMuX2ZpbGxCb3R0b21MaW5lQXRDZWxscyhlLHQpLHRoaXMuX2N0eC5yZXN0b3JlKCl9LHQucHJvdG90eXBlLl9yZW5kZXJCbHVyQ3Vyc29yPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9jdHguc2F2ZSgpLHRoaXMuX2N0eC5zdHJva2VTdHlsZT10aGlzLl9jb2xvcnMuY3Vyc29yLmNzcyx0aGlzLl9zdHJva2VSZWN0QXRDZWxsKGUsdCxyLmdldFdpZHRoKCksMSksdGhpcy5fY3R4LnJlc3RvcmUoKX0sbyhbcyg1LGwuSUJ1ZmZlclNlcnZpY2UpLHMoNixsLklPcHRpb25zU2VydmljZSkscyg3LGwuSUNvcmVTZXJ2aWNlKSxzKDgsdS5JQ29yZUJyb3dzZXJTZXJ2aWNlKV0sdCl9KGEuQmFzZVJlbmRlckxheWVyKTt0LkN1cnNvclJlbmRlckxheWVyPWY7dmFyIF89ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCl7dGhpcy5fcmVuZGVyQ2FsbGJhY2s9dCx0aGlzLmlzQ3Vyc29yVmlzaWJsZT0hMCxlJiZ0aGlzLl9yZXN0YXJ0SW50ZXJ2YWwoKX1yZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJpc1BhdXNlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiEodGhpcy5fYmxpbmtTdGFydFRpbWVvdXR8fHRoaXMuX2JsaW5rSW50ZXJ2YWwpfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt0aGlzLl9ibGlua0ludGVydmFsJiYod2luZG93LmNsZWFySW50ZXJ2YWwodGhpcy5fYmxpbmtJbnRlcnZhbCksdGhpcy5fYmxpbmtJbnRlcnZhbD12b2lkIDApLHRoaXMuX2JsaW5rU3RhcnRUaW1lb3V0JiYod2luZG93LmNsZWFyVGltZW91dCh0aGlzLl9ibGlua1N0YXJ0VGltZW91dCksdGhpcy5fYmxpbmtTdGFydFRpbWVvdXQ9dm9pZCAwKSx0aGlzLl9hbmltYXRpb25GcmFtZSYmKHdpbmRvdy5jYW5jZWxBbmltYXRpb25GcmFtZSh0aGlzLl9hbmltYXRpb25GcmFtZSksdGhpcy5fYW5pbWF0aW9uRnJhbWU9dm9pZCAwKX0sZS5wcm90b3R5cGUucmVzdGFydEJsaW5rQW5pbWF0aW9uPWZ1bmN0aW9uKCl7dmFyIGU9dGhpczt0aGlzLmlzUGF1c2VkfHwodGhpcy5fYW5pbWF0aW9uVGltZVJlc3RhcnRlZD1EYXRlLm5vdygpLHRoaXMuaXNDdXJzb3JWaXNpYmxlPSEwLHRoaXMuX2FuaW1hdGlvbkZyYW1lfHwodGhpcy5fYW5pbWF0aW9uRnJhbWU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoZnVuY3Rpb24oKXtlLl9yZW5kZXJDYWxsYmFjaygpLGUuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMH0pKSkpfSxlLnByb3RvdHlwZS5fcmVzdGFydEludGVydmFsPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7dm9pZCAwPT09ZSYmKGU9aCksdGhpcy5fYmxpbmtJbnRlcnZhbCYmKHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuX2JsaW5rSW50ZXJ2YWwpLHRoaXMuX2JsaW5rSW50ZXJ2YWw9dm9pZCAwKSx0aGlzLl9ibGlua1N0YXJ0VGltZW91dD13aW5kb3cuc2V0VGltZW91dCgoZnVuY3Rpb24oKXtpZih0Ll9hbmltYXRpb25UaW1lUmVzdGFydGVkKXt2YXIgZT1oLShEYXRlLm5vdygpLXQuX2FuaW1hdGlvblRpbWVSZXN0YXJ0ZWQpO2lmKHQuX2FuaW1hdGlvblRpbWVSZXN0YXJ0ZWQ9dm9pZCAwLGU+MClyZXR1cm4gdm9pZCB0Ll9yZXN0YXJ0SW50ZXJ2YWwoZSl9dC5pc0N1cnNvclZpc2libGU9ITEsdC5fYW5pbWF0aW9uRnJhbWU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoZnVuY3Rpb24oKXt0Ll9yZW5kZXJDYWxsYmFjaygpLHQuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMH0pKSx0Ll9ibGlua0ludGVydmFsPXdpbmRvdy5zZXRJbnRlcnZhbCgoZnVuY3Rpb24oKXtpZih0Ll9hbmltYXRpb25UaW1lUmVzdGFydGVkKXt2YXIgZT1oLShEYXRlLm5vdygpLXQuX2FuaW1hdGlvblRpbWVSZXN0YXJ0ZWQpO3JldHVybiB0Ll9hbmltYXRpb25UaW1lUmVzdGFydGVkPXZvaWQgMCx2b2lkIHQuX3Jlc3RhcnRJbnRlcnZhbChlKX10LmlzQ3Vyc29yVmlzaWJsZT0hdC5pc0N1cnNvclZpc2libGUsdC5fYW5pbWF0aW9uRnJhbWU9d2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoZnVuY3Rpb24oKXt0Ll9yZW5kZXJDYWxsYmFjaygpLHQuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMH0pKX0pLGgpfSksZSl9LGUucHJvdG90eXBlLnBhdXNlPWZ1bmN0aW9uKCl7dGhpcy5pc0N1cnNvclZpc2libGU9ITAsdGhpcy5fYmxpbmtJbnRlcnZhbCYmKHdpbmRvdy5jbGVhckludGVydmFsKHRoaXMuX2JsaW5rSW50ZXJ2YWwpLHRoaXMuX2JsaW5rSW50ZXJ2YWw9dm9pZCAwKSx0aGlzLl9ibGlua1N0YXJ0VGltZW91dCYmKHdpbmRvdy5jbGVhclRpbWVvdXQodGhpcy5fYmxpbmtTdGFydFRpbWVvdXQpLHRoaXMuX2JsaW5rU3RhcnRUaW1lb3V0PXZvaWQgMCksdGhpcy5fYW5pbWF0aW9uRnJhbWUmJih3aW5kb3cuY2FuY2VsQW5pbWF0aW9uRnJhbWUodGhpcy5fYW5pbWF0aW9uRnJhbWUpLHRoaXMuX2FuaW1hdGlvbkZyYW1lPXZvaWQgMCl9LGUucHJvdG90eXBlLnJlc3VtZT1mdW5jdGlvbigpe3RoaXMucGF1c2UoKSx0aGlzLl9hbmltYXRpb25UaW1lUmVzdGFydGVkPXZvaWQgMCx0aGlzLl9yZXN0YXJ0SW50ZXJ2YWwoKSx0aGlzLnJlc3RhcnRCbGlua0FuaW1hdGlvbigpfSxlfSgpfSw4OTc4OihlLHQscik9Pnt2YXIgaSxuLG8scyxhLGMsbCx1LGgsZixfLGQscCx2LGcseSxtLGIsUyxDLHcsTCxFLHgsQSxrLE0sUixULE8sQixELFAsSSxILGosRixXLFUscSxOLHosSyxWLEcsWSxYLFosSiwkLFEsZWUsdGUscmUsaWUsbmUsb2Usc2UsYWUsY2UsbGUsdWUsaGUsZmUsX2UsZGUscGUsdmUsZ2UseWUsbWUsYmUsU2UsQ2Usd2UsTGUsRWUseGUsQWUsa2UsTWUsUmUsVGUsT2UsQmUsRGUsUGUsSWUsSGUsamUsRmUsV2UsVWUscWUsTmUsemUsS2UsVmUsR2UsWWUsWGUsWmUsSmUsJGUsUWUsZXQsdHQscnQsaXQsbnQsb3Qsc3QsYXQsY3QsbHQsdXQsaHQsZnQsX3QsZHQscHQsdnQsZ3QseXQsbXQsYnQsU3QsQ3Q7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQudHJ5RHJhd0N1c3RvbUNoYXI9dC5ib3hEcmF3aW5nRGVmaW5pdGlvbnM9dC5ibG9ja0VsZW1lbnREZWZpbml0aW9ucz12b2lkIDA7dmFyIHd0PXIoMTc1Mik7dC5ibG9ja0VsZW1lbnREZWZpbml0aW9ucz17IuKWgCI6W3t4OjAseTowLHc6OCxoOjR9XSwi4paBIjpbe3g6MCx5Ojcsdzo4LGg6MX1dLCLiloIiOlt7eDowLHk6Nix3OjgsaDoyfV0sIuKWgyI6W3t4OjAseTo1LHc6OCxoOjN9XSwi4paEIjpbe3g6MCx5OjQsdzo4LGg6NH1dLCLiloUiOlt7eDowLHk6Myx3OjgsaDo1fV0sIuKWhiI6W3t4OjAseToyLHc6OCxoOjZ9XSwi4paHIjpbe3g6MCx5OjEsdzo4LGg6N31dLCLilogiOlt7eDowLHk6MCx3OjgsaDo4fV0sIuKWiSI6W3t4OjAseTowLHc6NyxoOjh9XSwi4paKIjpbe3g6MCx5OjAsdzo2LGg6OH1dLCLilosiOlt7eDowLHk6MCx3OjUsaDo4fV0sIuKWjCI6W3t4OjAseTowLHc6NCxoOjh9XSwi4paNIjpbe3g6MCx5OjAsdzozLGg6OH1dLCLilo4iOlt7eDowLHk6MCx3OjIsaDo4fV0sIuKWjyI6W3t4OjAseTowLHc6MSxoOjh9XSwi4paQIjpbe3g6NCx5OjAsdzo0LGg6OH1dLCLilpQiOlt7eDowLHk6MCx3OjksaDoxfV0sIuKWlSI6W3t4OjcseTowLHc6MSxoOjh9XSwi4paWIjpbe3g6MCx5OjQsdzo0LGg6NH1dLCLilpciOlt7eDo0LHk6NCx3OjQsaDo0fV0sIuKWmCI6W3t4OjAseTowLHc6NCxoOjR9XSwi4paZIjpbe3g6MCx5OjAsdzo0LGg6OH0se3g6MCx5OjQsdzo4LGg6NH1dLCLilpoiOlt7eDowLHk6MCx3OjQsaDo0fSx7eDo0LHk6NCx3OjQsaDo0fV0sIuKWmyI6W3t4OjAseTowLHc6NCxoOjh9LHt4OjAseTowLHc6NCxoOjh9XSwi4pacIjpbe3g6MCx5OjAsdzo4LGg6NH0se3g6NCx5OjAsdzo0LGg6OH1dLCLilp0iOlt7eDo0LHk6MCx3OjQsaDo0fV0sIuKWniI6W3t4OjQseTowLHc6NCxoOjR9LHt4OjAseTo0LHc6NCxoOjR9XSwi4pafIjpbe3g6NCx5OjAsdzo0LGg6OH0se3g6MCx5OjQsdzo4LGg6NH1dLCLwn62wIjpbe3g6MSx5OjAsdzoxLGg6OH1dLCLwn62xIjpbe3g6Mix5OjAsdzoxLGg6OH1dLCLwn62yIjpbe3g6Myx5OjAsdzoxLGg6OH1dLCLwn62zIjpbe3g6NCx5OjAsdzoxLGg6OH1dLCLwn620Ijpbe3g6NSx5OjAsdzoxLGg6OH1dLCLwn621Ijpbe3g6Nix5OjAsdzoxLGg6OH1dLCLwn622Ijpbe3g6MCx5OjEsdzo4LGg6MX1dLCLwn623Ijpbe3g6MCx5OjIsdzo4LGg6MX1dLCLwn624Ijpbe3g6MCx5OjMsdzo4LGg6MX1dLCLwn625Ijpbe3g6MCx5OjQsdzo4LGg6MX1dLCLwn626Ijpbe3g6MCx5OjUsdzo4LGg6MX1dLCLwn627Ijpbe3g6MCx5OjYsdzo4LGg6MX1dLCLwn628Ijpbe3g6MCx5OjAsdzoxLGg6OH0se3g6MCx5Ojcsdzo4LGg6MX1dLCLwn629Ijpbe3g6MCx5OjAsdzoxLGg6OH0se3g6MCx5OjAsdzo4LGg6MX1dLCLwn62+Ijpbe3g6Nyx5OjAsdzoxLGg6OH0se3g6MCx5OjAsdzo4LGg6MX1dLCLwn62/Ijpbe3g6Nyx5OjAsdzoxLGg6OH0se3g6MCx5Ojcsdzo4LGg6MX1dLCLwn66AIjpbe3g6MCx5OjAsdzo4LGg6MX0se3g6MCx5Ojcsdzo4LGg6MX1dLCLwn66BIjpbe3g6MCx5OjAsdzo4LGg6MX0se3g6MCx5OjIsdzo4LGg6MX0se3g6MCx5OjQsdzo4LGg6MX0se3g6MCx5Ojcsdzo4LGg6MX1dLCLwn66CIjpbe3g6MCx5OjAsdzo4LGg6Mn1dLCLwn66DIjpbe3g6MCx5OjAsdzo4LGg6M31dLCLwn66EIjpbe3g6MCx5OjAsdzo4LGg6NX1dLCLwn66FIjpbe3g6MCx5OjAsdzo4LGg6Nn1dLCLwn66GIjpbe3g6MCx5OjAsdzo4LGg6N31dLCLwn66HIjpbe3g6Nix5OjAsdzoyLGg6OH1dLCLwn66IIjpbe3g6NSx5OjAsdzozLGg6OH1dLCLwn66JIjpbe3g6Myx5OjAsdzo1LGg6OH1dLCLwn66KIjpbe3g6Mix5OjAsdzo2LGg6OH1dLCLwn66LIjpbe3g6MSx5OjAsdzo3LGg6OH1dLCLwn66VIjpbe3g6MCx5OjAsdzoyLGg6Mn0se3g6NCx5OjAsdzoyLGg6Mn0se3g6Mix5OjIsdzoyLGg6Mn0se3g6Nix5OjIsdzoyLGg6Mn0se3g6MCx5OjQsdzoyLGg6Mn0se3g6NCx5OjQsdzoyLGg6Mn0se3g6Mix5OjYsdzoyLGg6Mn0se3g6Nix5OjYsdzoyLGg6Mn1dLCLwn66WIjpbe3g6Mix5OjAsdzoyLGg6Mn0se3g6Nix5OjAsdzoyLGg6Mn0se3g6MCx5OjIsdzoyLGg6Mn0se3g6NCx5OjIsdzoyLGg6Mn0se3g6Mix5OjQsdzoyLGg6Mn0se3g6Nix5OjQsdzoyLGg6Mn0se3g6MCx5OjYsdzoyLGg6Mn0se3g6NCx5OjYsdzoyLGg6Mn1dLCLwn66XIjpbe3g6MCx5OjIsdzo4LGg6Mn0se3g6MCx5OjYsdzo4LGg6Mn1dfTt2YXIgTHQ9eyLilpEiOltbMSwwLDAsMF0sWzAsMCwwLDBdLFswLDAsMSwwXSxbMCwwLDAsMF1dLCLilpIiOltbMSwwXSxbMCwwXSxbMCwxXSxbMCwwXV0sIuKWkyI6W1swLDFdLFsxLDFdLFsxLDBdLFsxLDFdXX07dC5ib3hEcmF3aW5nRGVmaW5pdGlvbnM9eyLilIAiOihpPXt9LGlbMV09Ik0wLC41IEwxLC41IixpKSwi4pSBIjoobj17fSxuWzNdPSJNMCwuNSBMMSwuNSIsbiksIuKUgiI6KG89e30sb1sxXT0iTS41LDAgTC41LDEiLG8pLCLilIMiOihzPXt9LHNbM109Ik0uNSwwIEwuNSwxIixzKSwi4pSMIjooYT17fSxhWzFdPSJNMC41LDEgTC41LC41IEwxLC41IixhKSwi4pSPIjooYz17fSxjWzNdPSJNMC41LDEgTC41LC41IEwxLC41IixjKSwi4pSQIjoobD17fSxsWzFdPSJNMCwuNSBMLjUsLjUgTC41LDEiLGwpLCLilJMiOih1PXt9LHVbM109Ik0wLC41IEwuNSwuNSBMLjUsMSIsdSksIuKUlCI6KGg9e30saFsxXT0iTS41LDAgTC41LC41IEwxLC41IixoKSwi4pSXIjooZj17fSxmWzNdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLGYpLCLilJgiOihfPXt9LF9bMV09Ik0uNSwwIEwuNSwuNSBMMCwuNSIsXyksIuKUmyI6KGQ9e30sZFszXT0iTS41LDAgTC41LC41IEwwLC41IixkKSwi4pScIjoocD17fSxwWzFdPSJNLjUsMCBMLjUsMSBNLjUsLjUgTDEsLjUiLHApLCLilKMiOih2PXt9LHZbM109Ik0uNSwwIEwuNSwxIE0uNSwuNSBMMSwuNSIsdiksIuKUpCI6KGc9e30sZ1sxXT0iTS41LDAgTC41LDEgTS41LC41IEwwLC41IixnKSwi4pSrIjooeT17fSx5WzNdPSJNLjUsMCBMLjUsMSBNLjUsLjUgTDAsLjUiLHkpLCLilKwiOihtPXt9LG1bMV09Ik0wLC41IEwxLC41IE0uNSwuNSBMLjUsMSIsbSksIuKUsyI6KGI9e30sYlszXT0iTTAsLjUgTDEsLjUgTS41LC41IEwuNSwxIixiKSwi4pS0IjooUz17fSxTWzFdPSJNMCwuNSBMMSwuNSBNLjUsLjUgTC41LDAiLFMpLCLilLsiOihDPXt9LENbM109Ik0wLC41IEwxLC41IE0uNSwuNSBMLjUsMCIsQyksIuKUvCI6KHc9e30sd1sxXT0iTTAsLjUgTDEsLjUgTS41LDAgTC41LDEiLHcpLCLilYsiOihMPXt9LExbM109Ik0wLC41IEwxLC41IE0uNSwwIEwuNSwxIixMKSwi4pW0IjooRT17fSxFWzFdPSJNLjUsLjUgTDAsLjUiLEUpLCLilbgiOih4PXt9LHhbM109Ik0uNSwuNSBMMCwuNSIseCksIuKVtSI6KEE9e30sQVsxXT0iTS41LC41IEwuNSwwIixBKSwi4pW5Ijooaz17fSxrWzNdPSJNLjUsLjUgTC41LDAiLGspLCLilbYiOihNPXt9LE1bMV09Ik0uNSwuNSBMMSwuNSIsTSksIuKVuiI6KFI9e30sUlszXT0iTS41LC41IEwxLC41IixSKSwi4pW3IjooVD17fSxUWzFdPSJNLjUsLjUgTC41LDEiLFQpLCLilbsiOihPPXt9LE9bM109Ik0uNSwuNSBMLjUsMSIsTyksIuKVkCI6KEI9e30sQlsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwiKyguNS10KSsiIEwxLCIrKC41LXQpKyIgTTAsIisoLjUrdCkrIiBMMSwiKyguNSt0KX0sQiksIuKVkSI6KEQ9e30sRFsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNIisoLjUtZSkrIiwwIEwiKyguNS1lKSsiLDEgTSIrKC41K2UpKyIsMCBMIisoLjUrZSkrIiwxIn0sRCksIuKVkiI6KFA9e30sUFsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNLjUsMSBMLjUsIisoLjUtdCkrIiBMMSwiKyguNS10KSsiIE0uNSwiKyguNSt0KSsiIEwxLCIrKC41K3QpfSxQKSwi4pWTIjooST17fSxJWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0iKyguNS1lKSsiLDEgTCIrKC41LWUpKyIsLjUgTDEsLjUgTSIrKC41K2UpKyIsLjUgTCIrKC41K2UpKyIsMSJ9LEkpLCLilZQiOihIPXt9LEhbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTTEsIisoLjUtdCkrIiBMIisoLjUtZSkrIiwiKyguNS10KSsiIEwiKyguNS1lKSsiLDEgTTEsIisoLjUrdCkrIiBMIisoLjUrZSkrIiwiKyguNSt0KSsiIEwiKyguNStlKSsiLDEifSxIKSwi4pWVIjooaj17fSxqWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLCIrKC41LXQpKyIgTC41LCIrKC41LXQpKyIgTC41LDEgTTAsIisoLjUrdCkrIiBMLjUsIisoLjUrdCl9LGopLCLilZYiOihGPXt9LEZbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTSIrKC41K2UpKyIsMSBMIisoLjUrZSkrIiwuNSBMMCwuNSBNIisoLjUtZSkrIiwuNSBMIisoLjUtZSkrIiwxIn0sRiksIuKVlyI6KFc9e30sV1sxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwiKyguNSt0KSsiIEwiKyguNS1lKSsiLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsMSBNMCwiKyguNS10KSsiIEwiKyguNStlKSsiLCIrKC41LXQpKyIgTCIrKC41K2UpKyIsMSJ9LFcpLCLilZgiOihVPXt9LFVbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTS41LDAgTC41LCIrKC41K3QpKyIgTDEsIisoLjUrdCkrIiBNLjUsIisoLjUtdCkrIiBMMSwiKyguNS10KX0sVSksIuKVmSI6KHE9e30scVsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMSwuNSBMIisoLjUtZSkrIiwuNSBMIisoLjUtZSkrIiwwIE0iKyguNStlKSsiLC41IEwiKyguNStlKSsiLDAifSxxKSwi4pWaIjooTj17fSxOWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0xLCIrKC41LXQpKyIgTCIrKC41K2UpKyIsIisoLjUtdCkrIiBMIisoLjUrZSkrIiwwIE0xLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsIisoLjUrdCkrIiBMIisoLjUtZSkrIiwwIn0sTiksIuKVmyI6KHo9e30selsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwiKyguNSt0KSsiIEwuNSwiKyguNSt0KSsiIEwuNSwwIE0wLCIrKC41LXQpKyIgTC41LCIrKC41LXQpfSx6KSwi4pWcIjooSz17fSxLWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLC41IEwiKyguNStlKSsiLC41IEwiKyguNStlKSsiLDAgTSIrKC41LWUpKyIsLjUgTCIrKC41LWUpKyIsMCJ9LEspLCLilZ0iOihWPXt9LFZbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTTAsIisoLjUtdCkrIiBMIisoLjUtZSkrIiwiKyguNS10KSsiIEwiKyguNS1lKSsiLDAgTTAsIisoLjUrdCkrIiBMIisoLjUrZSkrIiwiKyguNSt0KSsiIEwiKyguNStlKSsiLDAifSxWKSwi4pWeIjooRz17fSxHWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0uNSwwIEwuNSwxIE0uNSwiKyguNS10KSsiIEwxLCIrKC41LXQpKyIgTS41LCIrKC41K3QpKyIgTDEsIisoLjUrdCl9LEcpLCLilZ8iOihZPXt9LFlbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTSIrKC41LWUpKyIsMCBMIisoLjUtZSkrIiwxIE0iKyguNStlKSsiLDAgTCIrKC41K2UpKyIsMSBNIisoLjUrZSkrIiwuNSBMMSwuNSJ9LFkpLCLilaAiOihYPXt9LFhbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTSIrKC41LWUpKyIsMCBMIisoLjUtZSkrIiwxIE0xLCIrKC41K3QpKyIgTCIrKC41K2UpKyIsIisoLjUrdCkrIiBMIisoLjUrZSkrIiwxIE0xLCIrKC41LXQpKyIgTCIrKC41K2UpKyIsIisoLjUtdCkrIiBMIisoLjUrZSkrIiwwIn0sWCksIuKVoSI6KFo9e30sWlsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNLjUsMCBMLjUsMSBNMCwiKyguNS10KSsiIEwuNSwiKyguNS10KSsiIE0wLCIrKC41K3QpKyIgTC41LCIrKC41K3QpfSxaKSwi4pWiIjooSj17fSxKWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLC41IEwiKyguNS1lKSsiLC41IE0iKyguNS1lKSsiLDAgTCIrKC41LWUpKyIsMSBNIisoLjUrZSkrIiwwIEwiKyguNStlKSsiLDEifSxKKSwi4pWjIjooJD17fSwkWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0iKyguNStlKSsiLDAgTCIrKC41K2UpKyIsMSBNMCwiKyguNSt0KSsiIEwiKyguNS1lKSsiLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsMSBNMCwiKyguNS10KSsiIEwiKyguNS1lKSsiLCIrKC41LXQpKyIgTCIrKC41LWUpKyIsMCJ9LCQpLCLilaQiOihRPXt9LFFbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTTAsIisoLjUtdCkrIiBMMSwiKyguNS10KSsiIE0wLCIrKC41K3QpKyIgTDEsIisoLjUrdCkrIiBNLjUsIisoLjUrdCkrIiBMLjUsMSJ9LFEpLCLilaUiOihlZT17fSxlZVsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwuNSBMMSwuNSBNIisoLjUtZSkrIiwuNSBMIisoLjUtZSkrIiwxIE0iKyguNStlKSsiLC41IEwiKyguNStlKSsiLDEifSxlZSksIuKVpiI6KHRlPXt9LHRlWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLCIrKC41LXQpKyIgTDEsIisoLjUtdCkrIiBNMCwiKyguNSt0KSsiIEwiKyguNS1lKSsiLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsMSBNMSwiKyguNSt0KSsiIEwiKyguNStlKSsiLCIrKC41K3QpKyIgTCIrKC41K2UpKyIsMSJ9LHRlKSwi4pWnIjoocmU9e30scmVbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTS41LDAgTC41LCIrKC41LXQpKyIgTTAsIisoLjUtdCkrIiBMMSwiKyguNS10KSsiIE0wLCIrKC41K3QpKyIgTDEsIisoLjUrdCl9LHJlKSwi4pWoIjooaWU9e30saWVbMV09ZnVuY3Rpb24oZSx0KXtyZXR1cm4iTTAsLjUgTDEsLjUgTSIrKC41LWUpKyIsLjUgTCIrKC41LWUpKyIsMCBNIisoLjUrZSkrIiwuNSBMIisoLjUrZSkrIiwwIn0saWUpLCLilakiOihuZT17fSxuZVsxXT1mdW5jdGlvbihlLHQpe3JldHVybiJNMCwiKyguNSt0KSsiIEwxLCIrKC41K3QpKyIgTTAsIisoLjUtdCkrIiBMIisoLjUtZSkrIiwiKyguNS10KSsiIEwiKyguNS1lKSsiLDAgTTEsIisoLjUtdCkrIiBMIisoLjUrZSkrIiwiKyguNS10KSsiIEwiKyguNStlKSsiLDAifSxuZSksIuKVqiI6KG9lPXt9LG9lWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0uNSwwIEwuNSwxIE0wLCIrKC41LXQpKyIgTDEsIisoLjUtdCkrIiBNMCwiKyguNSt0KSsiIEwxLCIrKC41K3QpfSxvZSksIuKVqyI6KHNlPXt9LHNlWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLC41IEwxLC41IE0iKyguNS1lKSsiLDAgTCIrKC41LWUpKyIsMSBNIisoLjUrZSkrIiwwIEwiKyguNStlKSsiLDEifSxzZSksIuKVrCI6KGFlPXt9LGFlWzFdPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIk0wLCIrKC41K3QpKyIgTCIrKC41LWUpKyIsIisoLjUrdCkrIiBMIisoLjUtZSkrIiwxIE0xLCIrKC41K3QpKyIgTCIrKC41K2UpKyIsIisoLjUrdCkrIiBMIisoLjUrZSkrIiwxIE0wLCIrKC41LXQpKyIgTCIrKC41LWUpKyIsIisoLjUtdCkrIiBMIisoLjUtZSkrIiwwIE0xLCIrKC41LXQpKyIgTCIrKC41K2UpKyIsIisoLjUtdCkrIiBMIisoLjUrZSkrIiwwIn0sYWUpLCLilbEiOihjZT17fSxjZVsxXT0iTTEsMCBMMCwxIixjZSksIuKVsiI6KGxlPXt9LGxlWzFdPSJNMCwwIEwxLDEiLGxlKSwi4pWzIjoodWU9e30sdWVbMV09Ik0xLDAgTDAsMSBNMCwwIEwxLDEiLHVlKSwi4pW8IjooaGU9e30saGVbMV09Ik0uNSwuNSBMMCwuNSIsaGVbM109Ik0uNSwuNSBMMSwuNSIsaGUpLCLilb0iOihmZT17fSxmZVsxXT0iTS41LC41IEwuNSwwIixmZVszXT0iTS41LC41IEwuNSwxIixmZSksIuKVviI6KF9lPXt9LF9lWzFdPSJNLjUsLjUgTDEsLjUiLF9lWzNdPSJNLjUsLjUgTDAsLjUiLF9lKSwi4pW/IjooZGU9e30sZGVbMV09Ik0uNSwuNSBMLjUsMSIsZGVbM109Ik0uNSwuNSBMLjUsMCIsZGUpLCLilI0iOihwZT17fSxwZVsxXT0iTS41LC41IEwuNSwxIixwZVszXT0iTS41LC41IEwxLC41IixwZSksIuKUjiI6KHZlPXt9LHZlWzFdPSJNLjUsLjUgTDEsLjUiLHZlWzNdPSJNLjUsLjUgTC41LDEiLHZlKSwi4pSRIjooZ2U9e30sZ2VbMV09Ik0uNSwuNSBMLjUsMSIsZ2VbM109Ik0uNSwuNSBMMCwuNSIsZ2UpLCLilJIiOih5ZT17fSx5ZVsxXT0iTS41LC41IEwwLC41Iix5ZVszXT0iTS41LC41IEwuNSwxIix5ZSksIuKUlSI6KG1lPXt9LG1lWzFdPSJNLjUsLjUgTC41LDAiLG1lWzNdPSJNLjUsLjUgTDEsLjUiLG1lKSwi4pSWIjooYmU9e30sYmVbMV09Ik0uNSwuNSBMMSwuNSIsYmVbM109Ik0uNSwuNSBMLjUsMCIsYmUpLCLilJkiOihTZT17fSxTZVsxXT0iTS41LC41IEwuNSwwIixTZVszXT0iTS41LC41IEwwLC41IixTZSksIuKUmiI6KENlPXt9LENlWzFdPSJNLjUsLjUgTDAsLjUiLENlWzNdPSJNLjUsLjUgTC41LDAiLENlKSwi4pSdIjood2U9e30sd2VbMV09Ik0uNSwwIEwuNSwxIix3ZVszXT0iTS41LC41IEwxLC41Iix3ZSksIuKUniI6KExlPXt9LExlWzFdPSJNMC41LDEgTC41LC41IEwxLC41IixMZVszXT0iTS41LC41IEwuNSwwIixMZSksIuKUnyI6KEVlPXt9LEVlWzFdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLEVlWzNdPSJNLjUsLjUgTC41LDEiLEVlKSwi4pSgIjooeGU9e30seGVbMV09Ik0uNSwuNSBMMSwuNSIseGVbM109Ik0uNSwwIEwuNSwxIix4ZSksIuKUoSI6KEFlPXt9LEFlWzFdPSJNLjUsLjUgTC41LDEiLEFlWzNdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLEFlKSwi4pSiIjooa2U9e30sa2VbMV09Ik0uNSwuNSBMLjUsMCIsa2VbM109Ik0wLjUsMSBMLjUsLjUgTDEsLjUiLGtlKSwi4pSlIjooTWU9e30sTWVbMV09Ik0uNSwwIEwuNSwxIixNZVszXT0iTS41LC41IEwwLC41IixNZSksIuKUpiI6KFJlPXt9LFJlWzFdPSJNMCwuNSBMLjUsLjUgTC41LDEiLFJlWzNdPSJNLjUsLjUgTC41LDAiLFJlKSwi4pSnIjooVGU9e30sVGVbMV09Ik0uNSwwIEwuNSwuNSBMMCwuNSIsVGVbM109Ik0uNSwuNSBMLjUsMSIsVGUpLCLilKgiOihPZT17fSxPZVsxXT0iTS41LC41IEwwLC41IixPZVszXT0iTS41LDAgTC41LDEiLE9lKSwi4pSpIjooQmU9e30sQmVbMV09Ik0uNSwuNSBMLjUsMSIsQmVbM109Ik0uNSwwIEwuNSwuNSBMMCwuNSIsQmUpLCLilKoiOihEZT17fSxEZVsxXT0iTS41LC41IEwuNSwwIixEZVszXT0iTTAsLjUgTC41LC41IEwuNSwxIixEZSksIuKUrSI6KFBlPXt9LFBlWzFdPSJNMC41LDEgTC41LC41IEwxLC41IixQZVszXT0iTS41LC41IEwwLC41IixQZSksIuKUriI6KEllPXt9LEllWzFdPSJNMCwuNSBMLjUsLjUgTC41LDEiLEllWzNdPSJNLjUsLjUgTDEsLjUiLEllKSwi4pSvIjooSGU9e30sSGVbMV09Ik0uNSwuNSBMLjUsMSIsSGVbM109Ik0wLC41IEwxLC41IixIZSksIuKUsCI6KGplPXt9LGplWzFdPSJNMCwuNSBMMSwuNSIsamVbM109Ik0uNSwuNSBMLjUsMSIsamUpLCLilLEiOihGZT17fSxGZVsxXT0iTS41LC41IEwxLC41IixGZVszXT0iTTAsLjUgTC41LC41IEwuNSwxIixGZSksIuKUsiI6KFdlPXt9LFdlWzFdPSJNLjUsLjUgTDAsLjUiLFdlWzNdPSJNMC41LDEgTC41LC41IEwxLC41IixXZSksIuKUtSI6KFVlPXt9LFVlWzFdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLFVlWzNdPSJNLjUsLjUgTDAsLjUiLFVlKSwi4pS2IjoocWU9e30scWVbMV09Ik0uNSwwIEwuNSwuNSBMMCwuNSIscWVbM109Ik0uNSwuNSBMMSwuNSIscWUpLCLilLciOihOZT17fSxOZVsxXT0iTS41LC41IEwuNSwwIixOZVszXT0iTTAsLjUgTDEsLjUiLE5lKSwi4pS4IjooemU9e30semVbMV09Ik0wLC41IEwxLC41Iix6ZVszXT0iTS41LC41IEwuNSwwIix6ZSksIuKUuSI6KEtlPXt9LEtlWzFdPSJNLjUsLjUgTDEsLjUiLEtlWzNdPSJNLjUsMCBMLjUsLjUgTDAsLjUiLEtlKSwi4pS6IjooVmU9e30sVmVbMV09Ik0uNSwuNSBMMCwuNSIsVmVbM109Ik0uNSwwIEwuNSwuNSBMMSwuNSIsVmUpLCLilL0iOihHZT17fSxHZVsxXT0iTS41LDAgTC41LDEgTS41LC41IEwxLC41IixHZVszXT0iTS41LC41IEwwLC41IixHZSksIuKUviI6KFllPXt9LFllWzFdPSJNLjUsMCBMLjUsMSBNLjUsLjUgTDAsLjUiLFllWzNdPSJNLjUsLjUgTDEsLjUiLFllKSwi4pS/IjooWGU9e30sWGVbMV09Ik0uNSwwIEwuNSwxIixYZVszXT0iTTAsLjUgTDEsLjUiLFhlKSwi4pWAIjooWmU9e30sWmVbMV09Ik0wLC41IEwxLC41IE0uNSwuNSBMLjUsMSIsWmVbM109Ik0uNSwuNSBMLjUsMCIsWmUpLCLilYEiOihKZT17fSxKZVsxXT0iTS41LC41IEwuNSwwIE0wLC41IEwxLC41IixKZVszXT0iTS41LC41IEwuNSwxIixKZSksIuKVgiI6KCRlPXt9LCRlWzFdPSJNMCwuNSBMMSwuNSIsJGVbM109Ik0uNSwwIEwuNSwxIiwkZSksIuKVgyI6KFFlPXt9LFFlWzFdPSJNMC41LDEgTC41LC41IEwxLC41IixRZVszXT0iTS41LDAgTC41LC41IEwwLC41IixRZSksIuKVhCI6KGV0PXt9LGV0WzFdPSJNMCwuNSBMLjUsLjUgTC41LDEiLGV0WzNdPSJNLjUsMCBMLjUsLjUgTDEsLjUiLGV0KSwi4pWFIjoodHQ9e30sdHRbMV09Ik0uNSwwIEwuNSwuNSBMMSwuNSIsdHRbM109Ik0wLC41IEwuNSwuNSBMLjUsMSIsdHQpLCLilYYiOihydD17fSxydFsxXT0iTS41LDAgTC41LC41IEwwLC41IixydFszXT0iTTAuNSwxIEwuNSwuNSBMMSwuNSIscnQpLCLilYciOihpdD17fSxpdFsxXT0iTS41LC41IEwuNSwxIixpdFszXT0iTS41LC41IEwuNSwwIE0wLC41IEwxLC41IixpdCksIuKViCI6KG50PXt9LG50WzFdPSJNLjUsLjUgTC41LDAiLG50WzNdPSJNMCwuNSBMMSwuNSBNLjUsLjUgTC41LDEiLG50KSwi4pWJIjoob3Q9e30sb3RbMV09Ik0uNSwuNSBMMSwuNSIsb3RbM109Ik0uNSwwIEwuNSwxIE0uNSwuNSBMMCwuNSIsb3QpLCLilYoiOihzdD17fSxzdFsxXT0iTS41LC41IEwwLC41IixzdFszXT0iTS41LDAgTC41LDEgTS41LC41IEwxLC41IixzdCksIuKVjCI6KGF0PXt9LGF0WzFdPSJNLjEsLjUgTC40LC41IE0uNiwuNSBMLjksLjUiLGF0KSwi4pWNIjooY3Q9e30sY3RbM109Ik0uMSwuNSBMLjQsLjUgTS42LC41IEwuOSwuNSIsY3QpLCLilIQiOihsdD17fSxsdFsxXT0iTS4wNjY3LC41IEwuMjY2NywuNSBNLjQsLjUgTC42LC41IE0uNzMzMywuNSBMLjkzMzMsLjUiLGx0KSwi4pSFIjoodXQ9e30sdXRbM109Ik0uMDY2NywuNSBMLjI2NjcsLjUgTS40LC41IEwuNiwuNSBNLjczMzMsLjUgTC45MzMzLC41Iix1dCksIuKUiCI6KGh0PXt9LGh0WzFdPSJNLjA1LC41IEwuMiwuNSBNLjMsLjUgTC40NSwuNSBNLjU1LC41IEwuNywuNSBNLjgsLjUgTC45NSwuNSIsaHQpLCLilIkiOihmdD17fSxmdFszXT0iTS4wNSwuNSBMLjIsLjUgTS4zLC41IEwuNDUsLjUgTS41NSwuNSBMLjcsLjUgTS44LC41IEwuOTUsLjUiLGZ0KSwi4pWOIjooX3Q9e30sX3RbMV09Ik0uNSwuMSBMLjUsLjQgTS41LC42IEwuNSwuOSIsX3QpLCLilY8iOihkdD17fSxkdFszXT0iTS41LC4xIEwuNSwuNCBNLjUsLjYgTC41LC45IixkdCksIuKUhiI6KHB0PXt9LHB0WzFdPSJNLjUsLjA2NjcgTC41LC4yNjY3IE0uNSwuNCBMLjUsLjYgTS41LC43MzMzIEwuNSwuOTMzMyIscHQpLCLilIciOih2dD17fSx2dFszXT0iTS41LC4wNjY3IEwuNSwuMjY2NyBNLjUsLjQgTC41LC42IE0uNSwuNzMzMyBMLjUsLjkzMzMiLHZ0KSwi4pSKIjooZ3Q9e30sZ3RbMV09Ik0uNSwuMDUgTC41LC4yIE0uNSwuMyBMLjUsLjQ1IEwuNSwuNTUgTS41LC43IEwuNSwuOTUiLGd0KSwi4pSLIjooeXQ9e30seXRbM109Ik0uNSwuMDUgTC41LC4yIE0uNSwuMyBMLjUsLjQ1IEwuNSwuNTUgTS41LC43IEwuNSwuOTUiLHl0KSwi4pWtIjoobXQ9e30sbXRbMV09IkMuNSwxLC41LC41LDEsLjUiLG10KSwi4pWuIjooYnQ9e30sYnRbMV09IkMuNSwxLC41LC41LDAsLjUiLGJ0KSwi4pWvIjooU3Q9e30sU3RbMV09IkMuNSwwLC41LC41LDAsLjUiLFN0KSwi4pWwIjooQ3Q9e30sQ3RbMV09IkMuNSwwLC41LC41LDEsLjUiLEN0KX0sdC50cnlEcmF3Q3VzdG9tQ2hhcj1mdW5jdGlvbihlLHIsaSxuLG8scyl7dmFyIGE9dC5ibG9ja0VsZW1lbnREZWZpbml0aW9uc1tyXTtpZihhKXJldHVybiBmdW5jdGlvbihlLHQscixpLG4sbyl7Zm9yKHZhciBzPTA7czx0Lmxlbmd0aDtzKyspe3ZhciBhPXRbc10sYz1uLzgsbD1vLzg7ZS5maWxsUmVjdChyK2EueCpjLGkrYS55KmwsYS53KmMsYS5oKmwpfX0oZSxhLGksbixvLHMpLCEwO3ZhciBjPUx0W3JdO2lmKGMpcmV0dXJuIGZ1bmN0aW9uKGUsdCxyLGksbixvKXt2YXIgcyxhPUV0LmdldCh0KTthfHwoYT1uZXcgTWFwLEV0LnNldCh0LGEpKTt2YXIgYz1lLmZpbGxTdHlsZTtpZigic3RyaW5nIiE9dHlwZW9mIGMpdGhyb3cgbmV3IEVycm9yKCdVbmV4cGVjdGVkIGZpbGxTdHlsZSB0eXBlICInK2MrJyInKTt2YXIgbD1hLmdldChjKTtpZighbCl7dmFyIHU9dFswXS5sZW5ndGgsaD10Lmxlbmd0aCxmPWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImNhbnZhcyIpO2Yud2lkdGg9dSxmLmhlaWdodD1oO3ZhciBfPSgwLHd0LnRocm93SWZGYWxzeSkoZi5nZXRDb250ZXh0KCIyZCIpKSxkPW5ldyBJbWFnZURhdGEodSxoKSxwPXZvaWQgMCx2PXZvaWQgMCxnPXZvaWQgMCx5PXZvaWQgMDtpZihjLnN0YXJ0c1dpdGgoIiMiKSlwPXBhcnNlSW50KGMuc3Vic3RyKDEsMiksMTYpLHY9cGFyc2VJbnQoYy5zdWJzdHIoMywyKSwxNiksZz1wYXJzZUludChjLnN1YnN0cig1LDIpLDE2KSx5PWMubGVuZ3RoPjcmJnBhcnNlSW50KGMuc3Vic3RyKDcsMiksMTYpfHwxO2Vsc2V7aWYoIWMuc3RhcnRzV2l0aCgicmdiYSIpKXRocm93IG5ldyBFcnJvcignVW5leHBlY3RlZCBmaWxsU3R5bGUgY29sb3IgZm9ybWF0ICInK2MrJyIgd2hlbiBkcmF3aW5nIHBhdHRlcm4gZ2x5cGgnKTtwPShzPWMuc3Vic3RyaW5nKDUsYy5sZW5ndGgtMSkuc3BsaXQoIiwiKS5tYXAoKGZ1bmN0aW9uKGUpe3JldHVybiBwYXJzZUZsb2F0KGUpfSkpKVswXSx2PXNbMV0sZz1zWzJdLHk9c1szXX1mb3IodmFyIG09MDttPGg7bSsrKWZvcih2YXIgYj0wO2I8dTtiKyspZC5kYXRhWzQqKG0qdStiKV09cCxkLmRhdGFbNCoobSp1K2IpKzFdPXYsZC5kYXRhWzQqKG0qdStiKSsyXT1nLGQuZGF0YVs0KihtKnUrYikrM109dFttXVtiXSooMjU1KnkpO18ucHV0SW1hZ2VEYXRhKGQsMCwwKSxsPSgwLHd0LnRocm93SWZGYWxzeSkoZS5jcmVhdGVQYXR0ZXJuKGYsbnVsbCkpLGEuc2V0KGMsbCl9ZS5maWxsU3R5bGU9bCxlLmZpbGxSZWN0KHIsaSxuLG8pfShlLGMsaSxuLG8scyksITA7dmFyIGw9dC5ib3hEcmF3aW5nRGVmaW5pdGlvbnNbcl07cmV0dXJuISFsJiYoZnVuY3Rpb24oZSx0LHIsaSxuLG8pe2Uuc3Ryb2tlU3R5bGU9ZS5maWxsU3R5bGU7Zm9yKHZhciBzPTAsYT1PYmplY3QuZW50cmllcyh0KTtzPGEubGVuZ3RoO3MrKyl7dmFyIGM9YVtzXSxsPWNbMF0sdT1jWzFdO2UuYmVnaW5QYXRoKCksZS5saW5lV2lkdGg9d2luZG93LmRldmljZVBpeGVsUmF0aW8qTnVtYmVyLnBhcnNlSW50KGwpO2Zvcih2YXIgaD0wLGY9KCJmdW5jdGlvbiI9PXR5cGVvZiB1P3UoLjE1LC4xNS9vKm4pOnUpLnNwbGl0KCIgIik7aDxmLmxlbmd0aDtoKyspe3ZhciBfPWZbaF0sZD1fWzBdLHA9QXRbZF07aWYocCl7dmFyIHY9Xy5zdWJzdHJpbmcoMSkuc3BsaXQoIiwiKTt2WzBdJiZ2WzFdJiZwKGUsa3QodixuLG8scixpKSl9ZWxzZSBjb25zb2xlLmVycm9yKCdDb3VsZCBub3QgZmluZCBkcmF3aW5nIGluc3RydWN0aW9ucyBmb3IgIicrZCsnIicpfWUuc3Ryb2tlKCksZS5jbG9zZVBhdGgoKX19KGUsbCxpLG4sbyxzKSwhMCl9O3ZhciBFdD1uZXcgTWFwO2Z1bmN0aW9uIHh0KGUsdCxyKXtyZXR1cm4gdm9pZCAwPT09ciYmKHI9MCksTWF0aC5tYXgoTWF0aC5taW4oZSx0KSxyKX12YXIgQXQ9e0M6ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZS5iZXppZXJDdXJ2ZVRvKHRbMF0sdFsxXSx0WzJdLHRbM10sdFs0XSx0WzVdKX0sTDpmdW5jdGlvbihlLHQpe3JldHVybiBlLmxpbmVUbyh0WzBdLHRbMV0pfSxNOmZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUubW92ZVRvKHRbMF0sdFsxXSl9fTtmdW5jdGlvbiBrdChlLHQscixpLG4pe3ZhciBvPWUubWFwKChmdW5jdGlvbihlKXtyZXR1cm4gcGFyc2VGbG9hdChlKXx8cGFyc2VJbnQoZSl9KSk7aWYoby5sZW5ndGg8Mil0aHJvdyBuZXcgRXJyb3IoIlRvbyBmZXcgYXJndW1lbnRzIGZvciBpbnN0cnVjdGlvbiIpO2Zvcih2YXIgcz0wO3M8by5sZW5ndGg7cys9MilvW3NdKj10LDAhPT1vW3NdJiYob1tzXT14dChNYXRoLnJvdW5kKG9bc10rLjUpLS41LHQsMCkpLG9bc10rPWk7Zm9yKHZhciBhPTE7YTxvLmxlbmd0aDthKz0yKW9bYV0qPXIsMCE9PW9bYV0mJihvW2FdPXh0KE1hdGgucm91bmQob1thXSsuNSktLjUsciwwKSksb1thXSs9bjtyZXR1cm4gb319LDM3MDA6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5HcmlkQ2FjaGU9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuY2FjaGU9W119cmV0dXJuIGUucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe2Zvcih2YXIgcj0wO3I8ZTtyKyspe3RoaXMuY2FjaGUubGVuZ3RoPD1yJiZ0aGlzLmNhY2hlLnB1c2goW10pO2Zvcih2YXIgaT10aGlzLmNhY2hlW3JdLmxlbmd0aDtpPHQ7aSsrKXRoaXMuY2FjaGVbcl0ucHVzaCh2b2lkIDApO3RoaXMuY2FjaGVbcl0ubGVuZ3RoPXR9dGhpcy5jYWNoZS5sZW5ndGg9ZX0sZS5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXtmb3IodmFyIGU9MDtlPHRoaXMuY2FjaGUubGVuZ3RoO2UrKylmb3IodmFyIHQ9MDt0PHRoaXMuY2FjaGVbZV0ubGVuZ3RoO3QrKyl0aGlzLmNhY2hlW2VdW3RdPXZvaWQgMH0sZX0oKTt0LkdyaWRDYWNoZT1yfSw1MDk4OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkxpbmtSZW5kZXJMYXllcj12b2lkIDA7dmFyIGE9cigxNTQ2KSxjPXIoODgwMyksbD1yKDIwNDApLHU9cigyNTg1KSxoPWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyLGksbixvLHMsYSxjKXt2YXIgbD1lLmNhbGwodGhpcyx0LCJsaW5rIixyLCEwLGksbixhLGMpfHx0aGlzO3JldHVybiBvLm9uU2hvd0xpbmtVbmRlcmxpbmUoKGZ1bmN0aW9uKGUpe3JldHVybiBsLl9vblNob3dMaW5rVW5kZXJsaW5lKGUpfSkpLG8ub25IaWRlTGlua1VuZGVybGluZSgoZnVuY3Rpb24oZSl7cmV0dXJuIGwuX29uSGlkZUxpbmtVbmRlcmxpbmUoZSl9KSkscy5vblNob3dMaW5rVW5kZXJsaW5lKChmdW5jdGlvbihlKXtyZXR1cm4gbC5fb25TaG93TGlua1VuZGVybGluZShlKX0pKSxzLm9uSGlkZUxpbmtVbmRlcmxpbmUoKGZ1bmN0aW9uKGUpe3JldHVybiBsLl9vbkhpZGVMaW5rVW5kZXJsaW5lKGUpfSkpLGx9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5yZXNpemU9ZnVuY3Rpb24odCl7ZS5wcm90b3R5cGUucmVzaXplLmNhbGwodGhpcyx0KSx0aGlzLl9zdGF0ZT12b2lkIDB9LHQucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5fY2xlYXJDdXJyZW50TGluaygpfSx0LnByb3RvdHlwZS5fY2xlYXJDdXJyZW50TGluaz1mdW5jdGlvbigpe2lmKHRoaXMuX3N0YXRlKXt0aGlzLl9jbGVhckNlbGxzKHRoaXMuX3N0YXRlLngxLHRoaXMuX3N0YXRlLnkxLHRoaXMuX3N0YXRlLmNvbHMtdGhpcy5fc3RhdGUueDEsMSk7dmFyIGU9dGhpcy5fc3RhdGUueTItdGhpcy5fc3RhdGUueTEtMTtlPjAmJnRoaXMuX2NsZWFyQ2VsbHMoMCx0aGlzLl9zdGF0ZS55MSsxLHRoaXMuX3N0YXRlLmNvbHMsZSksdGhpcy5fY2xlYXJDZWxscygwLHRoaXMuX3N0YXRlLnkyLHRoaXMuX3N0YXRlLngyLDEpLHRoaXMuX3N0YXRlPXZvaWQgMH19LHQucHJvdG90eXBlLl9vblNob3dMaW5rVW5kZXJsaW5lPWZ1bmN0aW9uKGUpe2lmKGUuZmc9PT1jLklOVkVSVEVEX0RFRkFVTFRfQ09MT1I/dGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuYmFja2dyb3VuZC5jc3M6ZS5mZyYmKDAsbC5pczI1NkNvbG9yKShlLmZnKT90aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5hbnNpW2UuZmddLmNzczp0aGlzLl9jdHguZmlsbFN0eWxlPXRoaXMuX2NvbG9ycy5mb3JlZ3JvdW5kLmNzcyxlLnkxPT09ZS55Mil0aGlzLl9maWxsQm90dG9tTGluZUF0Q2VsbHMoZS54MSxlLnkxLGUueDItZS54MSk7ZWxzZXt0aGlzLl9maWxsQm90dG9tTGluZUF0Q2VsbHMoZS54MSxlLnkxLGUuY29scy1lLngxKTtmb3IodmFyIHQ9ZS55MSsxO3Q8ZS55Mjt0KyspdGhpcy5fZmlsbEJvdHRvbUxpbmVBdENlbGxzKDAsdCxlLmNvbHMpO3RoaXMuX2ZpbGxCb3R0b21MaW5lQXRDZWxscygwLGUueTIsZS54Mil9dGhpcy5fc3RhdGU9ZX0sdC5wcm90b3R5cGUuX29uSGlkZUxpbmtVbmRlcmxpbmU9ZnVuY3Rpb24oZSl7dGhpcy5fY2xlYXJDdXJyZW50TGluaygpfSxvKFtzKDYsdS5JQnVmZmVyU2VydmljZSkscyg3LHUuSU9wdGlvbnNTZXJ2aWNlKV0sdCl9KGEuQmFzZVJlbmRlckxheWVyKTt0LkxpbmtSZW5kZXJMYXllcj1ofSwzNTI1OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlJlbmRlcmVyPXZvaWQgMDt2YXIgYT1yKDk1OTYpLGM9cig0MTQ5KSxsPXIoMjUxMiksdT1yKDUwOTgpLGg9cig4NDQpLGY9cig0NzI1KSxfPXIoMjU4NSksZD1yKDE0MjApLHA9cig4NDYwKSx2PTEsZz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpLG4sbyxzLGgsZil7dmFyIF89ZS5jYWxsKHRoaXMpfHx0aGlzO18uX2NvbG9ycz10LF8uX3NjcmVlbkVsZW1lbnQ9cixfLl9idWZmZXJTZXJ2aWNlPXMsXy5fY2hhclNpemVTZXJ2aWNlPWgsXy5fb3B0aW9uc1NlcnZpY2U9ZixfLl9pZD12KyssXy5fb25SZXF1ZXN0UmVkcmF3PW5ldyBwLkV2ZW50RW1pdHRlcjt2YXIgZD1fLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmFsbG93VHJhbnNwYXJlbmN5O3JldHVybiBfLl9yZW5kZXJMYXllcnM9W28uY3JlYXRlSW5zdGFuY2UoYS5UZXh0UmVuZGVyTGF5ZXIsXy5fc2NyZWVuRWxlbWVudCwwLF8uX2NvbG9ycyxkLF8uX2lkKSxvLmNyZWF0ZUluc3RhbmNlKGMuU2VsZWN0aW9uUmVuZGVyTGF5ZXIsXy5fc2NyZWVuRWxlbWVudCwxLF8uX2NvbG9ycyxfLl9pZCksby5jcmVhdGVJbnN0YW5jZSh1LkxpbmtSZW5kZXJMYXllcixfLl9zY3JlZW5FbGVtZW50LDIsXy5fY29sb3JzLF8uX2lkLGksbiksby5jcmVhdGVJbnN0YW5jZShsLkN1cnNvclJlbmRlckxheWVyLF8uX3NjcmVlbkVsZW1lbnQsMyxfLl9jb2xvcnMsXy5faWQsXy5fb25SZXF1ZXN0UmVkcmF3KV0sXy5kaW1lbnNpb25zPXtzY2FsZWRDaGFyV2lkdGg6MCxzY2FsZWRDaGFySGVpZ2h0OjAsc2NhbGVkQ2VsbFdpZHRoOjAsc2NhbGVkQ2VsbEhlaWdodDowLHNjYWxlZENoYXJMZWZ0OjAsc2NhbGVkQ2hhclRvcDowLHNjYWxlZENhbnZhc1dpZHRoOjAsc2NhbGVkQ2FudmFzSGVpZ2h0OjAsY2FudmFzV2lkdGg6MCxjYW52YXNIZWlnaHQ6MCxhY3R1YWxDZWxsV2lkdGg6MCxhY3R1YWxDZWxsSGVpZ2h0OjB9LF8uX2RldmljZVBpeGVsUmF0aW89d2luZG93LmRldmljZVBpeGVsUmF0aW8sXy5fdXBkYXRlRGltZW5zaW9ucygpLF8ub25PcHRpb25zQ2hhbmdlZCgpLF99cmV0dXJuIG4odCxlKSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uUmVxdWVzdFJlZHJhdyIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RSZWRyYXcuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe2Zvcih2YXIgdD0wLHI9dGhpcy5fcmVuZGVyTGF5ZXJzO3Q8ci5sZW5ndGg7dCsrKXJbdF0uZGlzcG9zZSgpO2UucHJvdG90eXBlLmRpc3Bvc2UuY2FsbCh0aGlzKSwoMCxkLnJlbW92ZVRlcm1pbmFsRnJvbUNhY2hlKSh0aGlzLl9pZCl9LHQucHJvdG90eXBlLm9uRGV2aWNlUGl4ZWxSYXRpb0NoYW5nZT1mdW5jdGlvbigpe3RoaXMuX2RldmljZVBpeGVsUmF0aW8hPT13aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyYmKHRoaXMuX2RldmljZVBpeGVsUmF0aW89d2luZG93LmRldmljZVBpeGVsUmF0aW8sdGhpcy5vblJlc2l6ZSh0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKSl9LHQucHJvdG90eXBlLnNldENvbG9ycz1mdW5jdGlvbihlKXt0aGlzLl9jb2xvcnM9ZTtmb3IodmFyIHQ9MCxyPXRoaXMuX3JlbmRlckxheWVyczt0PHIubGVuZ3RoO3QrKyl7dmFyIGk9clt0XTtpLnNldENvbG9ycyh0aGlzLl9jb2xvcnMpLGkucmVzZXQoKX19LHQucHJvdG90eXBlLm9uUmVzaXplPWZ1bmN0aW9uKGUsdCl7dGhpcy5fdXBkYXRlRGltZW5zaW9ucygpO2Zvcih2YXIgcj0wLGk9dGhpcy5fcmVuZGVyTGF5ZXJzO3I8aS5sZW5ndGg7cisrKWlbcl0ucmVzaXplKHRoaXMuZGltZW5zaW9ucyk7dGhpcy5fc2NyZWVuRWxlbWVudC5zdHlsZS53aWR0aD10aGlzLmRpbWVuc2lvbnMuY2FudmFzV2lkdGgrInB4Iix0aGlzLl9zY3JlZW5FbGVtZW50LnN0eWxlLmhlaWdodD10aGlzLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0KyJweCJ9LHQucHJvdG90eXBlLm9uQ2hhclNpemVDaGFuZ2VkPWZ1bmN0aW9uKCl7dGhpcy5vblJlc2l6ZSh0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKX0sdC5wcm90b3R5cGUub25CbHVyPWZ1bmN0aW9uKCl7dGhpcy5fcnVuT3BlcmF0aW9uKChmdW5jdGlvbihlKXtyZXR1cm4gZS5vbkJsdXIoKX0pKX0sdC5wcm90b3R5cGUub25Gb2N1cz1mdW5jdGlvbigpe3RoaXMuX3J1bk9wZXJhdGlvbigoZnVuY3Rpb24oZSl7cmV0dXJuIGUub25Gb2N1cygpfSkpfSx0LnByb3RvdHlwZS5vblNlbGVjdGlvbkNoYW5nZWQ9ZnVuY3Rpb24oZSx0LHIpe3ZvaWQgMD09PXImJihyPSExKSx0aGlzLl9ydW5PcGVyYXRpb24oKGZ1bmN0aW9uKGkpe3JldHVybiBpLm9uU2VsZWN0aW9uQ2hhbmdlZChlLHQscil9KSl9LHQucHJvdG90eXBlLm9uQ3Vyc29yTW92ZT1mdW5jdGlvbigpe3RoaXMuX3J1bk9wZXJhdGlvbigoZnVuY3Rpb24oZSl7cmV0dXJuIGUub25DdXJzb3JNb3ZlKCl9KSl9LHQucHJvdG90eXBlLm9uT3B0aW9uc0NoYW5nZWQ9ZnVuY3Rpb24oKXt0aGlzLl9ydW5PcGVyYXRpb24oKGZ1bmN0aW9uKGUpe3JldHVybiBlLm9uT3B0aW9uc0NoYW5nZWQoKX0pKX0sdC5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXt0aGlzLl9ydW5PcGVyYXRpb24oKGZ1bmN0aW9uKGUpe3JldHVybiBlLnJlc2V0KCl9KSl9LHQucHJvdG90eXBlLl9ydW5PcGVyYXRpb249ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PTAscj10aGlzLl9yZW5kZXJMYXllcnM7dDxyLmxlbmd0aDt0KyspZShyW3RdKX0sdC5wcm90b3R5cGUucmVuZGVyUm93cz1mdW5jdGlvbihlLHQpe2Zvcih2YXIgcj0wLGk9dGhpcy5fcmVuZGVyTGF5ZXJzO3I8aS5sZW5ndGg7cisrKWlbcl0ub25HcmlkQ2hhbmdlZChlLHQpfSx0LnByb3RvdHlwZS5jbGVhclRleHR1cmVBdGxhcz1mdW5jdGlvbigpe2Zvcih2YXIgZT0wLHQ9dGhpcy5fcmVuZGVyTGF5ZXJzO2U8dC5sZW5ndGg7ZSsrKXRbZV0uY2xlYXJUZXh0dXJlQXRsYXMoKX0sdC5wcm90b3R5cGUuX3VwZGF0ZURpbWVuc2lvbnM9ZnVuY3Rpb24oKXt0aGlzLl9jaGFyU2l6ZVNlcnZpY2UuaGFzVmFsaWRTaXplJiYodGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJXaWR0aD1NYXRoLmZsb29yKHRoaXMuX2NoYXJTaXplU2VydmljZS53aWR0aCp3aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyksdGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJIZWlnaHQ9TWF0aC5jZWlsKHRoaXMuX2NoYXJTaXplU2VydmljZS5oZWlnaHQqd2luZG93LmRldmljZVBpeGVsUmF0aW8pLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsSGVpZ2h0PU1hdGguZmxvb3IodGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJIZWlnaHQqdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5saW5lSGVpZ2h0KSx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2hhclRvcD0xPT09dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5saW5lSGVpZ2h0PzA6TWF0aC5yb3VuZCgodGhpcy5kaW1lbnNpb25zLnNjYWxlZENlbGxIZWlnaHQtdGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJIZWlnaHQpLzIpLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsV2lkdGg9dGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJXaWR0aCtNYXRoLnJvdW5kKHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMubGV0dGVyU3BhY2luZyksdGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJMZWZ0PU1hdGguZmxvb3IodGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5sZXR0ZXJTcGFjaW5nLzIpLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDYW52YXNIZWlnaHQ9dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKnRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsSGVpZ2h0LHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDYW52YXNXaWR0aD10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMqdGhpcy5kaW1lbnNpb25zLnNjYWxlZENlbGxXaWR0aCx0aGlzLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0PU1hdGgucm91bmQodGhpcy5kaW1lbnNpb25zLnNjYWxlZENhbnZhc0hlaWdodC93aW5kb3cuZGV2aWNlUGl4ZWxSYXRpbyksdGhpcy5kaW1lbnNpb25zLmNhbnZhc1dpZHRoPU1hdGgucm91bmQodGhpcy5kaW1lbnNpb25zLnNjYWxlZENhbnZhc1dpZHRoL3dpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKSx0aGlzLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbEhlaWdodD10aGlzLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0L3RoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyx0aGlzLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoPXRoaXMuZGltZW5zaW9ucy5jYW52YXNXaWR0aC90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpfSxvKFtzKDQsXy5JSW5zdGFudGlhdGlvblNlcnZpY2UpLHMoNSxfLklCdWZmZXJTZXJ2aWNlKSxzKDYsZi5JQ2hhclNpemVTZXJ2aWNlKSxzKDcsXy5JT3B0aW9uc1NlcnZpY2UpXSx0KX0oaC5EaXNwb3NhYmxlKTt0LlJlbmRlcmVyPWd9LDE3NTI6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC50aHJvd0lmRmFsc3k9dm9pZCAwLHQudGhyb3dJZkZhbHN5PWZ1bmN0aW9uKGUpe2lmKCFlKXRocm93IG5ldyBFcnJvcigidmFsdWUgbXVzdCBub3QgYmUgZmFsc3kiKTtyZXR1cm4gZX19LDQxNDk6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSksbz10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LHM9dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuU2VsZWN0aW9uUmVuZGVyTGF5ZXI9dm9pZCAwO3ZhciBhPXIoMTU0NiksYz1yKDI1ODUpLGw9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8scyl7dmFyIGE9ZS5jYWxsKHRoaXMsdCwic2VsZWN0aW9uIixyLCEwLGksbixvLHMpfHx0aGlzO3JldHVybiBhLl9jbGVhclN0YXRlKCksYX1yZXR1cm4gbih0LGUpLHQucHJvdG90eXBlLl9jbGVhclN0YXRlPWZ1bmN0aW9uKCl7dGhpcy5fc3RhdGU9e3N0YXJ0OnZvaWQgMCxlbmQ6dm9pZCAwLGNvbHVtblNlbGVjdE1vZGU6dm9pZCAwLHlkaXNwOnZvaWQgMH19LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbih0KXtlLnByb3RvdHlwZS5yZXNpemUuY2FsbCh0aGlzLHQpLHRoaXMuX2NsZWFyU3RhdGUoKX0sdC5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLl9zdGF0ZS5zdGFydCYmdGhpcy5fc3RhdGUuZW5kJiYodGhpcy5fY2xlYXJTdGF0ZSgpLHRoaXMuX2NsZWFyQWxsKCkpfSx0LnByb3RvdHlwZS5vblNlbGVjdGlvbkNoYW5nZWQ9ZnVuY3Rpb24oZSx0LHIpe2lmKHRoaXMuX2RpZFN0YXRlQ2hhbmdlKGUsdCxyLHRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwKSlpZih0aGlzLl9jbGVhckFsbCgpLGUmJnQpe3ZhciBpPWVbMV0tdGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3Asbj10WzFdLXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwLG89TWF0aC5tYXgoaSwwKSxzPU1hdGgubWluKG4sdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEpO2lmKG8+PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93c3x8czwwKXRoaXMuX3N0YXRlLnlkaXNwPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwO2Vsc2V7aWYodGhpcy5fY3R4LmZpbGxTdHlsZT10aGlzLl9jb2xvcnMuc2VsZWN0aW9uVHJhbnNwYXJlbnQuY3NzLHIpe3ZhciBhPWVbMF0sYz10WzBdLWEsbD1zLW8rMTt0aGlzLl9maWxsQ2VsbHMoYSxvLGMsbCl9ZWxzZXthPWk9PT1vP2VbMF06MDt2YXIgdT1vPT09bj90WzBdOnRoaXMuX2J1ZmZlclNlcnZpY2UuY29sczt0aGlzLl9maWxsQ2VsbHMoYSxvLHUtYSwxKTt2YXIgaD1NYXRoLm1heChzLW8tMSwwKTtpZih0aGlzLl9maWxsQ2VsbHMoMCxvKzEsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGgpLG8hPT1zKXt2YXIgZj1uPT09cz90WzBdOnRoaXMuX2J1ZmZlclNlcnZpY2UuY29sczt0aGlzLl9maWxsQ2VsbHMoMCxzLGYsMSl9fXRoaXMuX3N0YXRlLnN0YXJ0PVtlWzBdLGVbMV1dLHRoaXMuX3N0YXRlLmVuZD1bdFswXSx0WzFdXSx0aGlzLl9zdGF0ZS5jb2x1bW5TZWxlY3RNb2RlPXIsdGhpcy5fc3RhdGUueWRpc3A9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3B9fWVsc2UgdGhpcy5fY2xlYXJTdGF0ZSgpfSx0LnByb3RvdHlwZS5fZGlkU3RhdGVDaGFuZ2U9ZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuIXRoaXMuX2FyZUNvb3JkaW5hdGVzRXF1YWwoZSx0aGlzLl9zdGF0ZS5zdGFydCl8fCF0aGlzLl9hcmVDb29yZGluYXRlc0VxdWFsKHQsdGhpcy5fc3RhdGUuZW5kKXx8ciE9PXRoaXMuX3N0YXRlLmNvbHVtblNlbGVjdE1vZGV8fGkhPT10aGlzLl9zdGF0ZS55ZGlzcH0sdC5wcm90b3R5cGUuX2FyZUNvb3JkaW5hdGVzRXF1YWw9ZnVuY3Rpb24oZSx0KXtyZXR1cm4hKCFlfHwhdCkmJmVbMF09PT10WzBdJiZlWzFdPT09dFsxXX0sbyhbcyg0LGMuSUJ1ZmZlclNlcnZpY2UpLHMoNSxjLklPcHRpb25zU2VydmljZSldLHQpfShhLkJhc2VSZW5kZXJMYXllcik7dC5TZWxlY3Rpb25SZW5kZXJMYXllcj1sfSw5NTk2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlRleHRSZW5kZXJMYXllcj12b2lkIDA7dmFyIGE9cigzNzAwKSxjPXIoMTU0NiksbD1yKDM3MzQpLHU9cig2NDMpLGg9cig1MTEpLGY9cigyNTg1KSxfPXIoNDcyNSksZD1yKDQyNjkpLHA9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8scyxjLGwpe3ZhciB1PWUuY2FsbCh0aGlzLHQsInRleHQiLHIsbixpLG8scyxjKXx8dGhpcztyZXR1cm4gdS5fY2hhcmFjdGVySm9pbmVyU2VydmljZT1sLHUuX2NoYXJhY3RlcldpZHRoPTAsdS5fY2hhcmFjdGVyRm9udD0iIix1Ll9jaGFyYWN0ZXJPdmVybGFwQ2FjaGU9e30sdS5fd29ya0NlbGw9bmV3IGguQ2VsbERhdGEsdS5fc3RhdGU9bmV3IGEuR3JpZENhY2hlLHV9cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5yZXNpemU9ZnVuY3Rpb24odCl7ZS5wcm90b3R5cGUucmVzaXplLmNhbGwodGhpcyx0KTt2YXIgcj10aGlzLl9nZXRGb250KCExLCExKTt0aGlzLl9jaGFyYWN0ZXJXaWR0aD09PXQuc2NhbGVkQ2hhcldpZHRoJiZ0aGlzLl9jaGFyYWN0ZXJGb250PT09cnx8KHRoaXMuX2NoYXJhY3RlcldpZHRoPXQuc2NhbGVkQ2hhcldpZHRoLHRoaXMuX2NoYXJhY3RlckZvbnQ9cix0aGlzLl9jaGFyYWN0ZXJPdmVybGFwQ2FjaGU9e30pLHRoaXMuX3N0YXRlLmNsZWFyKCksdGhpcy5fc3RhdGUucmVzaXplKHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MpfSx0LnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe3RoaXMuX3N0YXRlLmNsZWFyKCksdGhpcy5fY2xlYXJBbGwoKX0sdC5wcm90b3R5cGUuX2ZvckVhY2hDZWxsPWZ1bmN0aW9uKGUsdCxyKXtmb3IodmFyIGk9ZTtpPD10O2krKylmb3IodmFyIG49aSt0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCxvPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLmxpbmVzLmdldChuKSxzPXRoaXMuX2NoYXJhY3RlckpvaW5lclNlcnZpY2UuZ2V0Sm9pbmVkQ2hhcmFjdGVycyhuKSxhPTA7YTx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM7YSsrKXtvLmxvYWRDZWxsKGEsdGhpcy5fd29ya0NlbGwpO3ZhciBjPXRoaXMuX3dvcmtDZWxsLGw9ITEsaD1hO2lmKDAhPT1jLmdldFdpZHRoKCkpe2lmKHMubGVuZ3RoPjAmJmE9PT1zWzBdWzBdKXtsPSEwO3ZhciBmPXMuc2hpZnQoKTtjPW5ldyBkLkpvaW5lZENlbGxEYXRhKHRoaXMuX3dvcmtDZWxsLG8udHJhbnNsYXRlVG9TdHJpbmcoITAsZlswXSxmWzFdKSxmWzFdLWZbMF0pLGg9ZlsxXS0xfSFsJiZ0aGlzLl9pc092ZXJsYXBwaW5nKGMpJiZoPG8ubGVuZ3RoLTEmJm8uZ2V0Q29kZVBvaW50KGgrMSk9PT11Lk5VTExfQ0VMTF9DT0RFJiYoYy5jb250ZW50Jj0tMTI1ODI5MTMsYy5jb250ZW50fD0yPDwyMikscihjLGEsaSksYT1ofX19LHQucHJvdG90eXBlLl9kcmF3QmFja2dyb3VuZD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMsaT10aGlzLl9jdHgsbj10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsbz0wLHM9MCxhPW51bGw7aS5zYXZlKCksdGhpcy5fZm9yRWFjaENlbGwoZSx0LChmdW5jdGlvbihlLHQsYyl7dmFyIHU9bnVsbDtlLmlzSW52ZXJzZSgpP3U9ZS5pc0ZnRGVmYXVsdCgpP3IuX2NvbG9ycy5mb3JlZ3JvdW5kLmNzczplLmlzRmdSR0IoKT8icmdiKCIrbC5BdHRyaWJ1dGVEYXRhLnRvQ29sb3JSR0IoZS5nZXRGZ0NvbG9yKCkpLmpvaW4oIiwiKSsiKSI6ci5fY29sb3JzLmFuc2lbZS5nZXRGZ0NvbG9yKCldLmNzczplLmlzQmdSR0IoKT91PSJyZ2IoIitsLkF0dHJpYnV0ZURhdGEudG9Db2xvclJHQihlLmdldEJnQ29sb3IoKSkuam9pbigiLCIpKyIpIjplLmlzQmdQYWxldHRlKCkmJih1PXIuX2NvbG9ycy5hbnNpW2UuZ2V0QmdDb2xvcigpXS5jc3MpLG51bGw9PT1hJiYobz10LHM9YyksYyE9PXM/KGkuZmlsbFN0eWxlPWF8fCIiLHIuX2ZpbGxDZWxscyhvLHMsbi1vLDEpLG89dCxzPWMpOmEhPT11JiYoaS5maWxsU3R5bGU9YXx8IiIsci5fZmlsbENlbGxzKG8scyx0LW8sMSksbz10LHM9YyksYT11fSkpLG51bGwhPT1hJiYoaS5maWxsU3R5bGU9YSx0aGlzLl9maWxsQ2VsbHMobyxzLG4tbywxKSksaS5yZXN0b3JlKCl9LHQucHJvdG90eXBlLl9kcmF3Rm9yZWdyb3VuZD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXM7dGhpcy5fZm9yRWFjaENlbGwoZSx0LChmdW5jdGlvbihlLHQsaSl7aWYoIWUuaXNJbnZpc2libGUoKSYmKHIuX2RyYXdDaGFycyhlLHQsaSksZS5pc1VuZGVybGluZSgpfHxlLmlzU3RyaWtldGhyb3VnaCgpKSl7aWYoci5fY3R4LnNhdmUoKSxlLmlzSW52ZXJzZSgpKWlmKGUuaXNCZ0RlZmF1bHQoKSlyLl9jdHguZmlsbFN0eWxlPXIuX2NvbG9ycy5iYWNrZ3JvdW5kLmNzcztlbHNlIGlmKGUuaXNCZ1JHQigpKXIuX2N0eC5maWxsU3R5bGU9InJnYigiK2wuQXR0cmlidXRlRGF0YS50b0NvbG9yUkdCKGUuZ2V0QmdDb2xvcigpKS5qb2luKCIsIikrIikiO2Vsc2V7dmFyIG49ZS5nZXRCZ0NvbG9yKCk7ci5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5kcmF3Qm9sZFRleHRJbkJyaWdodENvbG9ycyYmZS5pc0JvbGQoKSYmbjw4JiYobis9OCksci5fY3R4LmZpbGxTdHlsZT1yLl9jb2xvcnMuYW5zaVtuXS5jc3N9ZWxzZSBpZihlLmlzRmdEZWZhdWx0KCkpci5fY3R4LmZpbGxTdHlsZT1yLl9jb2xvcnMuZm9yZWdyb3VuZC5jc3M7ZWxzZSBpZihlLmlzRmdSR0IoKSlyLl9jdHguZmlsbFN0eWxlPSJyZ2IoIitsLkF0dHJpYnV0ZURhdGEudG9Db2xvclJHQihlLmdldEZnQ29sb3IoKSkuam9pbigiLCIpKyIpIjtlbHNle3ZhciBvPWUuZ2V0RmdDb2xvcigpO3IuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZHJhd0JvbGRUZXh0SW5CcmlnaHRDb2xvcnMmJmUuaXNCb2xkKCkmJm88OCYmKG8rPTgpLHIuX2N0eC5maWxsU3R5bGU9ci5fY29sb3JzLmFuc2lbb10uY3NzfWUuaXNTdHJpa2V0aHJvdWdoKCkmJnIuX2ZpbGxNaWRkbGVMaW5lQXRDZWxscyh0LGksZS5nZXRXaWR0aCgpKSxlLmlzVW5kZXJsaW5lKCkmJnIuX2ZpbGxCb3R0b21MaW5lQXRDZWxscyh0LGksZS5nZXRXaWR0aCgpKSxyLl9jdHgucmVzdG9yZSgpfX0pKX0sdC5wcm90b3R5cGUub25HcmlkQ2hhbmdlZD1mdW5jdGlvbihlLHQpezAhPT10aGlzLl9zdGF0ZS5jYWNoZS5sZW5ndGgmJih0aGlzLl9jaGFyQXRsYXMmJnRoaXMuX2NoYXJBdGxhcy5iZWdpbkZyYW1lKCksdGhpcy5fY2xlYXJDZWxscygwLGUsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLHQtZSsxKSx0aGlzLl9kcmF3QmFja2dyb3VuZChlLHQpLHRoaXMuX2RyYXdGb3JlZ3JvdW5kKGUsdCkpfSx0LnByb3RvdHlwZS5vbk9wdGlvbnNDaGFuZ2VkPWZ1bmN0aW9uKCl7dGhpcy5fc2V0VHJhbnNwYXJlbmN5KHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuYWxsb3dUcmFuc3BhcmVuY3kpfSx0LnByb3RvdHlwZS5faXNPdmVybGFwcGluZz1mdW5jdGlvbihlKXtpZigxIT09ZS5nZXRXaWR0aCgpKXJldHVybiExO2lmKGUuZ2V0Q29kZSgpPDI1NilyZXR1cm4hMTt2YXIgdD1lLmdldENoYXJzKCk7aWYodGhpcy5fY2hhcmFjdGVyT3ZlcmxhcENhY2hlLmhhc093blByb3BlcnR5KHQpKXJldHVybiB0aGlzLl9jaGFyYWN0ZXJPdmVybGFwQ2FjaGVbdF07dGhpcy5fY3R4LnNhdmUoKSx0aGlzLl9jdHguZm9udD10aGlzLl9jaGFyYWN0ZXJGb250O3ZhciByPU1hdGguZmxvb3IodGhpcy5fY3R4Lm1lYXN1cmVUZXh0KHQpLndpZHRoKT50aGlzLl9jaGFyYWN0ZXJXaWR0aDtyZXR1cm4gdGhpcy5fY3R4LnJlc3RvcmUoKSx0aGlzLl9jaGFyYWN0ZXJPdmVybGFwQ2FjaGVbdF09cixyfSxvKFtzKDUsZi5JQnVmZmVyU2VydmljZSkscyg2LGYuSU9wdGlvbnNTZXJ2aWNlKSxzKDcsXy5JQ2hhcmFjdGVySm9pbmVyU2VydmljZSldLHQpfShjLkJhc2VSZW5kZXJMYXllcik7dC5UZXh0UmVuZGVyTGF5ZXI9cH0sOTYxNjooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJhc2VDaGFyQXRsYXM9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX2RpZFdhcm1VcD0hMX1yZXR1cm4gZS5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe30sZS5wcm90b3R5cGUud2FybVVwPWZ1bmN0aW9uKCl7dGhpcy5fZGlkV2FybVVwfHwodGhpcy5fZG9XYXJtVXAoKSx0aGlzLl9kaWRXYXJtVXA9ITApfSxlLnByb3RvdHlwZS5fZG9XYXJtVXA9ZnVuY3Rpb24oKXt9LGUucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7fSxlLnByb3RvdHlwZS5iZWdpbkZyYW1lPWZ1bmN0aW9uKCl7fSxlfSgpO3QuQmFzZUNoYXJBdGxhcz1yfSwxNDIwOihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5yZW1vdmVUZXJtaW5hbEZyb21DYWNoZT10LmFjcXVpcmVDaGFyQXRsYXM9dm9pZCAwO3ZhciBpPXIoMjA0MCksbj1yKDE5MDYpLG89W107dC5hY3F1aXJlQ2hhckF0bGFzPWZ1bmN0aW9uKGUsdCxyLHMsYSl7Zm9yKHZhciBjPSgwLGkuZ2VuZXJhdGVDb25maWcpKHMsYSxlLHIpLGw9MDtsPG8ubGVuZ3RoO2wrKyl7dmFyIHU9KGg9b1tsXSkub3duZWRCeS5pbmRleE9mKHQpO2lmKHU+PTApe2lmKCgwLGkuY29uZmlnRXF1YWxzKShoLmNvbmZpZyxjKSlyZXR1cm4gaC5hdGxhczsxPT09aC5vd25lZEJ5Lmxlbmd0aD8oaC5hdGxhcy5kaXNwb3NlKCksby5zcGxpY2UobCwxKSk6aC5vd25lZEJ5LnNwbGljZSh1LDEpO2JyZWFrfX1mb3IobD0wO2w8by5sZW5ndGg7bCsrKXt2YXIgaD1vW2xdO2lmKCgwLGkuY29uZmlnRXF1YWxzKShoLmNvbmZpZyxjKSlyZXR1cm4gaC5vd25lZEJ5LnB1c2godCksaC5hdGxhc312YXIgZj17YXRsYXM6bmV3IG4uRHluYW1pY0NoYXJBdGxhcyhkb2N1bWVudCxjKSxjb25maWc6Yyxvd25lZEJ5Olt0XX07cmV0dXJuIG8ucHVzaChmKSxmLmF0bGFzfSx0LnJlbW92ZVRlcm1pbmFsRnJvbUNhY2hlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8by5sZW5ndGg7dCsrKXt2YXIgcj1vW3RdLm93bmVkQnkuaW5kZXhPZihlKTtpZigtMSE9PXIpezE9PT1vW3RdLm93bmVkQnkubGVuZ3RoPyhvW3RdLmF0bGFzLmRpc3Bvc2UoKSxvLnNwbGljZSh0LDEpKTpvW3RdLm93bmVkQnkuc3BsaWNlKHIsMSk7YnJlYWt9fX19LDIwNDA6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19zcHJlYWRBcnJheXx8ZnVuY3Rpb24oZSx0LHIpe2lmKHJ8fDI9PT1hcmd1bWVudHMubGVuZ3RoKWZvcih2YXIgaSxuPTAsbz10Lmxlbmd0aDtuPG87bisrKSFpJiZuIGluIHR8fChpfHwoaT1BcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCh0LDAsbikpLGlbbl09dFtuXSk7cmV0dXJuIGUuY29uY2F0KGl8fEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHQpKX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuaXMyNTZDb2xvcj10LmNvbmZpZ0VxdWFscz10LmdlbmVyYXRlQ29uZmlnPXZvaWQgMDt2YXIgbj1yKDY0Myk7dC5nZW5lcmF0ZUNvbmZpZz1mdW5jdGlvbihlLHQscixuKXt2YXIgbz17Zm9yZWdyb3VuZDpuLmZvcmVncm91bmQsYmFja2dyb3VuZDpuLmJhY2tncm91bmQsY3Vyc29yOnZvaWQgMCxjdXJzb3JBY2NlbnQ6dm9pZCAwLHNlbGVjdGlvbjp2b2lkIDAsYW5zaTppKFtdLG4uYW5zaSwhMCl9O3JldHVybntkZXZpY2VQaXhlbFJhdGlvOndpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHNjYWxlZENoYXJXaWR0aDplLHNjYWxlZENoYXJIZWlnaHQ6dCxmb250RmFtaWx5OnIuZm9udEZhbWlseSxmb250U2l6ZTpyLmZvbnRTaXplLGZvbnRXZWlnaHQ6ci5mb250V2VpZ2h0LGZvbnRXZWlnaHRCb2xkOnIuZm9udFdlaWdodEJvbGQsYWxsb3dUcmFuc3BhcmVuY3k6ci5hbGxvd1RyYW5zcGFyZW5jeSxjb2xvcnM6b319LHQuY29uZmlnRXF1YWxzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPTA7cjxlLmNvbG9ycy5hbnNpLmxlbmd0aDtyKyspaWYoZS5jb2xvcnMuYW5zaVtyXS5yZ2JhIT09dC5jb2xvcnMuYW5zaVtyXS5yZ2JhKXJldHVybiExO3JldHVybiBlLmRldmljZVBpeGVsUmF0aW89PT10LmRldmljZVBpeGVsUmF0aW8mJmUuZm9udEZhbWlseT09PXQuZm9udEZhbWlseSYmZS5mb250U2l6ZT09PXQuZm9udFNpemUmJmUuZm9udFdlaWdodD09PXQuZm9udFdlaWdodCYmZS5mb250V2VpZ2h0Qm9sZD09PXQuZm9udFdlaWdodEJvbGQmJmUuYWxsb3dUcmFuc3BhcmVuY3k9PT10LmFsbG93VHJhbnNwYXJlbmN5JiZlLnNjYWxlZENoYXJXaWR0aD09PXQuc2NhbGVkQ2hhcldpZHRoJiZlLnNjYWxlZENoYXJIZWlnaHQ9PT10LnNjYWxlZENoYXJIZWlnaHQmJmUuY29sb3JzLmZvcmVncm91bmQ9PT10LmNvbG9ycy5mb3JlZ3JvdW5kJiZlLmNvbG9ycy5iYWNrZ3JvdW5kPT09dC5jb2xvcnMuYmFja2dyb3VuZH0sdC5pczI1NkNvbG9yPWZ1bmN0aW9uKGUpe3JldHVybiBlPG4uREVGQVVMVF9DT0xPUn19LDg4MDM6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkNIQVJfQVRMQVNfQ0VMTF9TUEFDSU5HPXQuVEVYVF9CQVNFTElORT10LkRJTV9PUEFDSVRZPXQuSU5WRVJURURfREVGQVVMVF9DT0xPUj12b2lkIDA7dmFyIGk9cig2MTE0KTt0LklOVkVSVEVEX0RFRkFVTFRfQ09MT1I9MjU3LHQuRElNX09QQUNJVFk9LjUsdC5URVhUX0JBU0VMSU5FPWkuaXNGaXJlZm94PyJib3R0b20iOiJpZGVvZ3JhcGhpYyIsdC5DSEFSX0FUTEFTX0NFTExfU1BBQ0lORz0xfSwxOTA2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0Lk5vbmVDaGFyQXRsYXM9dC5EeW5hbWljQ2hhckF0bGFzPXQuZ2V0R2x5cGhDYWNoZUtleT12b2lkIDA7dmFyIG89cig4ODAzKSxzPXIoOTYxNiksYT1yKDU2ODApLGM9cig3MDAxKSxsPXIoNjExNCksdT1yKDE3NTIpLGg9cig0Nzc0KSxmPTEwMjQsXz0xMDI0LGQ9e2NzczoicmdiYSgwLCAwLCAwLCAwKSIscmdiYTowfTtmdW5jdGlvbiBwKGUpe3JldHVybiBlLmNvZGU8PDIxfGUuYmc8PDEyfGUuZmc8PDN8KGUuYm9sZD8wOjQpKyhlLmRpbT8wOjIpKyhlLml0YWxpYz8wOjEpfXQuZ2V0R2x5cGhDYWNoZUtleT1wO3ZhciB2PWZ1bmN0aW9uKGUpe2Z1bmN0aW9uIHQodCxyKXt2YXIgaT1lLmNhbGwodGhpcyl8fHRoaXM7aS5fY29uZmlnPXIsaS5fZHJhd1RvQ2FjaGVDb3VudD0wLGkuX2dseXBoc1dhaXRpbmdPbkJpdG1hcD1bXSxpLl9iaXRtYXBDb21taXRUaW1lb3V0PW51bGwsaS5fYml0bWFwPW51bGwsaS5fY2FjaGVDYW52YXM9dC5jcmVhdGVFbGVtZW50KCJjYW52YXMiKSxpLl9jYWNoZUNhbnZhcy53aWR0aD1mLGkuX2NhY2hlQ2FudmFzLmhlaWdodD1fLGkuX2NhY2hlQ3R4PSgwLHUudGhyb3dJZkZhbHN5KShpLl9jYWNoZUNhbnZhcy5nZXRDb250ZXh0KCIyZCIse2FscGhhOiEwfSkpO3ZhciBuPXQuY3JlYXRlRWxlbWVudCgiY2FudmFzIik7bi53aWR0aD1pLl9jb25maWcuc2NhbGVkQ2hhcldpZHRoLG4uaGVpZ2h0PWkuX2NvbmZpZy5zY2FsZWRDaGFySGVpZ2h0LGkuX3RtcEN0eD0oMCx1LnRocm93SWZGYWxzeSkobi5nZXRDb250ZXh0KCIyZCIse2FscGhhOmkuX2NvbmZpZy5hbGxvd1RyYW5zcGFyZW5jeX0pKSxpLl93aWR0aD1NYXRoLmZsb29yKGYvaS5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCksaS5faGVpZ2h0PU1hdGguZmxvb3IoXy9pLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCk7dmFyIG89aS5fd2lkdGgqaS5faGVpZ2h0O3JldHVybiBpLl9jYWNoZU1hcD1uZXcgYy5MUlVNYXAobyksaS5fY2FjaGVNYXAucHJlYWxsb2MobyksaX1yZXR1cm4gbih0LGUpLHQucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXtudWxsIT09dGhpcy5fYml0bWFwQ29tbWl0VGltZW91dCYmKHdpbmRvdy5jbGVhclRpbWVvdXQodGhpcy5fYml0bWFwQ29tbWl0VGltZW91dCksdGhpcy5fYml0bWFwQ29tbWl0VGltZW91dD1udWxsKX0sdC5wcm90b3R5cGUuYmVnaW5GcmFtZT1mdW5jdGlvbigpe3RoaXMuX2RyYXdUb0NhY2hlQ291bnQ9MH0sdC5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXtpZih0aGlzLl9jYWNoZU1hcC5zaXplPjApe3ZhciBlPXRoaXMuX3dpZHRoKnRoaXMuX2hlaWdodDt0aGlzLl9jYWNoZU1hcD1uZXcgYy5MUlVNYXAoZSksdGhpcy5fY2FjaGVNYXAucHJlYWxsb2MoZSl9dGhpcy5fY2FjaGVDdHguY2xlYXJSZWN0KDAsMCxmLF8pLHRoaXMuX3RtcEN0eC5jbGVhclJlY3QoMCwwLHRoaXMuX2NvbmZpZy5zY2FsZWRDaGFyV2lkdGgsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJIZWlnaHQpfSx0LnByb3RvdHlwZS5kcmF3PWZ1bmN0aW9uKGUsdCxyLGkpe2lmKDMyPT09dC5jb2RlKXJldHVybiEwO2lmKCF0aGlzLl9jYW5DYWNoZSh0KSlyZXR1cm4hMTt2YXIgbj1wKHQpLG89dGhpcy5fY2FjaGVNYXAuZ2V0KG4pO2lmKG51bGwhPW8pcmV0dXJuIHRoaXMuX2RyYXdGcm9tQ2FjaGUoZSxvLHIsaSksITA7aWYodGhpcy5fZHJhd1RvQ2FjaGVDb3VudDwxMDApe3ZhciBzO3M9dGhpcy5fY2FjaGVNYXAuc2l6ZTx0aGlzLl9jYWNoZU1hcC5jYXBhY2l0eT90aGlzLl9jYWNoZU1hcC5zaXplOnRoaXMuX2NhY2hlTWFwLnBlZWsoKS5pbmRleDt2YXIgYT10aGlzLl9kcmF3VG9DYWNoZSh0LHMpO3JldHVybiB0aGlzLl9jYWNoZU1hcC5zZXQobixhKSx0aGlzLl9kcmF3RnJvbUNhY2hlKGUsYSxyLGkpLCEwfXJldHVybiExfSx0LnByb3RvdHlwZS5fY2FuQ2FjaGU9ZnVuY3Rpb24oZSl7cmV0dXJuIGUuY29kZTwyNTZ9LHQucHJvdG90eXBlLl90b0Nvb3JkaW5hdGVYPWZ1bmN0aW9uKGUpe3JldHVybiBlJXRoaXMuX3dpZHRoKnRoaXMuX2NvbmZpZy5zY2FsZWRDaGFyV2lkdGh9LHQucHJvdG90eXBlLl90b0Nvb3JkaW5hdGVZPWZ1bmN0aW9uKGUpe3JldHVybiBNYXRoLmZsb29yKGUvdGhpcy5fd2lkdGgpKnRoaXMuX2NvbmZpZy5zY2FsZWRDaGFySGVpZ2h0fSx0LnByb3RvdHlwZS5fZHJhd0Zyb21DYWNoZT1mdW5jdGlvbihlLHQscixpKXtpZighdC5pc0VtcHR5KXt2YXIgbj10aGlzLl90b0Nvb3JkaW5hdGVYKHQuaW5kZXgpLG89dGhpcy5fdG9Db29yZGluYXRlWSh0LmluZGV4KTtlLmRyYXdJbWFnZSh0LmluQml0bWFwP3RoaXMuX2JpdG1hcDp0aGlzLl9jYWNoZUNhbnZhcyxuLG8sdGhpcy5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCx0aGlzLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCxyLGksdGhpcy5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCx0aGlzLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCl9fSx0LnByb3RvdHlwZS5fZ2V0Q29sb3JGcm9tQW5zaUluZGV4PWZ1bmN0aW9uKGUpe3JldHVybiBlPHRoaXMuX2NvbmZpZy5jb2xvcnMuYW5zaS5sZW5ndGg/dGhpcy5fY29uZmlnLmNvbG9ycy5hbnNpW2VdOmEuREVGQVVMVF9BTlNJX0NPTE9SU1tlXX0sdC5wcm90b3R5cGUuX2dldEJhY2tncm91bmRDb2xvcj1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fY29uZmlnLmFsbG93VHJhbnNwYXJlbmN5P2Q6ZS5iZz09PW8uSU5WRVJURURfREVGQVVMVF9DT0xPUj90aGlzLl9jb25maWcuY29sb3JzLmZvcmVncm91bmQ6ZS5iZzwyNTY/dGhpcy5fZ2V0Q29sb3JGcm9tQW5zaUluZGV4KGUuYmcpOnRoaXMuX2NvbmZpZy5jb2xvcnMuYmFja2dyb3VuZH0sdC5wcm90b3R5cGUuX2dldEZvcmVncm91bmRDb2xvcj1mdW5jdGlvbihlKXtyZXR1cm4gZS5mZz09PW8uSU5WRVJURURfREVGQVVMVF9DT0xPUj9oLmNvbG9yLm9wYXF1ZSh0aGlzLl9jb25maWcuY29sb3JzLmJhY2tncm91bmQpOmUuZmc8MjU2P3RoaXMuX2dldENvbG9yRnJvbUFuc2lJbmRleChlLmZnKTp0aGlzLl9jb25maWcuY29sb3JzLmZvcmVncm91bmR9LHQucHJvdG90eXBlLl9kcmF3VG9DYWNoZT1mdW5jdGlvbihlLHQpe3RoaXMuX2RyYXdUb0NhY2hlQ291bnQrKyx0aGlzLl90bXBDdHguc2F2ZSgpO3ZhciByPXRoaXMuX2dldEJhY2tncm91bmRDb2xvcihlKTt0aGlzLl90bXBDdHguZ2xvYmFsQ29tcG9zaXRlT3BlcmF0aW9uPSJjb3B5Iix0aGlzLl90bXBDdHguZmlsbFN0eWxlPXIuY3NzLHRoaXMuX3RtcEN0eC5maWxsUmVjdCgwLDAsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCx0aGlzLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCksdGhpcy5fdG1wQ3R4Lmdsb2JhbENvbXBvc2l0ZU9wZXJhdGlvbj0ic291cmNlLW92ZXIiO3ZhciBpPWUuYm9sZD90aGlzLl9jb25maWcuZm9udFdlaWdodEJvbGQ6dGhpcy5fY29uZmlnLmZvbnRXZWlnaHQsbj1lLml0YWxpYz8iaXRhbGljIjoiIjt0aGlzLl90bXBDdHguZm9udD1uKyIgIitpKyIgIit0aGlzLl9jb25maWcuZm9udFNpemUqdGhpcy5fY29uZmlnLmRldmljZVBpeGVsUmF0aW8rInB4ICIrdGhpcy5fY29uZmlnLmZvbnRGYW1pbHksdGhpcy5fdG1wQ3R4LnRleHRCYXNlbGluZT1vLlRFWFRfQkFTRUxJTkUsdGhpcy5fdG1wQ3R4LmZpbGxTdHlsZT10aGlzLl9nZXRGb3JlZ3JvdW5kQ29sb3IoZSkuY3NzLGUuZGltJiYodGhpcy5fdG1wQ3R4Lmdsb2JhbEFscGhhPW8uRElNX09QQUNJVFkpLHRoaXMuX3RtcEN0eC5maWxsVGV4dChlLmNoYXJzLDAsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJIZWlnaHQpO3ZhciBzPXRoaXMuX3RtcEN0eC5nZXRJbWFnZURhdGEoMCwwLHRoaXMuX2NvbmZpZy5zY2FsZWRDaGFyV2lkdGgsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJIZWlnaHQpLGE9ITE7aWYodGhpcy5fY29uZmlnLmFsbG93VHJhbnNwYXJlbmN5fHwoYT15KHMscikpLGEmJiJfIj09PWUuY2hhcnMmJiF0aGlzLl9jb25maWcuYWxsb3dUcmFuc3BhcmVuY3kpZm9yKHZhciBjPTE7Yzw9NSYmKHRoaXMuX3RtcEN0eC5maWxsVGV4dChlLmNoYXJzLDAsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJIZWlnaHQtYyksYT15KHM9dGhpcy5fdG1wQ3R4LmdldEltYWdlRGF0YSgwLDAsdGhpcy5fY29uZmlnLnNjYWxlZENoYXJXaWR0aCx0aGlzLl9jb25maWcuc2NhbGVkQ2hhckhlaWdodCkscikpO2MrKyk7dGhpcy5fdG1wQ3R4LnJlc3RvcmUoKTt2YXIgbD10aGlzLl90b0Nvb3JkaW5hdGVYKHQpLHU9dGhpcy5fdG9Db29yZGluYXRlWSh0KTt0aGlzLl9jYWNoZUN0eC5wdXRJbWFnZURhdGEocyxsLHUpO3ZhciBoPXtpbmRleDp0LGlzRW1wdHk6YSxpbkJpdG1hcDohMX07cmV0dXJuIHRoaXMuX2FkZEdseXBoVG9CaXRtYXAoaCksaH0sdC5wcm90b3R5cGUuX2FkZEdseXBoVG9CaXRtYXA9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpczshKCJjcmVhdGVJbWFnZUJpdG1hcCJpbiB3aW5kb3cpfHxsLmlzRmlyZWZveHx8bC5pc1NhZmFyaXx8KHRoaXMuX2dseXBoc1dhaXRpbmdPbkJpdG1hcC5wdXNoKGUpLG51bGw9PT10aGlzLl9iaXRtYXBDb21taXRUaW1lb3V0JiYodGhpcy5fYml0bWFwQ29tbWl0VGltZW91dD13aW5kb3cuc2V0VGltZW91dCgoZnVuY3Rpb24oKXtyZXR1cm4gdC5fZ2VuZXJhdGVCaXRtYXAoKX0pLDEwMCkpKX0sdC5wcm90b3R5cGUuX2dlbmVyYXRlQml0bWFwPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcyx0PXRoaXMuX2dseXBoc1dhaXRpbmdPbkJpdG1hcDt0aGlzLl9nbHlwaHNXYWl0aW5nT25CaXRtYXA9W10sd2luZG93LmNyZWF0ZUltYWdlQml0bWFwKHRoaXMuX2NhY2hlQ2FudmFzKS50aGVuKChmdW5jdGlvbihyKXtlLl9iaXRtYXA9cjtmb3IodmFyIGk9MDtpPHQubGVuZ3RoO2krKyl0W2ldLmluQml0bWFwPSEwfSkpLHRoaXMuX2JpdG1hcENvbW1pdFRpbWVvdXQ9bnVsbH0sdH0ocy5CYXNlQ2hhckF0bGFzKTt0LkR5bmFtaWNDaGFyQXRsYXM9djt2YXIgZz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscil7cmV0dXJuIGUuY2FsbCh0aGlzKXx8dGhpc31yZXR1cm4gbih0LGUpLHQucHJvdG90eXBlLmRyYXc9ZnVuY3Rpb24oZSx0LHIsaSl7cmV0dXJuITF9LHR9KHMuQmFzZUNoYXJBdGxhcyk7ZnVuY3Rpb24geShlLHQpe2Zvcih2YXIgcj0hMCxpPXQucmdiYT4+PjI0LG49dC5yZ2JhPj4+MTYmMjU1LG89dC5yZ2JhPj4+OCYyNTUscz0wO3M8ZS5kYXRhLmxlbmd0aDtzKz00KWUuZGF0YVtzXT09PWkmJmUuZGF0YVtzKzFdPT09biYmZS5kYXRhW3MrMl09PT1vP2UuZGF0YVtzKzNdPTA6cj0hMTtyZXR1cm4gcn10Lk5vbmVDaGFyQXRsYXM9Z30sNzAwMTooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkxSVU1hcD12b2lkIDA7dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe3RoaXMuY2FwYWNpdHk9ZSx0aGlzLl9tYXA9e30sdGhpcy5faGVhZD1udWxsLHRoaXMuX3RhaWw9bnVsbCx0aGlzLl9ub2RlUG9vbD1bXSx0aGlzLnNpemU9MH1yZXR1cm4gZS5wcm90b3R5cGUuX3VubGlua05vZGU9ZnVuY3Rpb24oZSl7dmFyIHQ9ZS5wcmV2LHI9ZS5uZXh0O2U9PT10aGlzLl9oZWFkJiYodGhpcy5faGVhZD1yKSxlPT09dGhpcy5fdGFpbCYmKHRoaXMuX3RhaWw9dCksbnVsbCE9PXQmJih0Lm5leHQ9ciksbnVsbCE9PXImJihyLnByZXY9dCl9LGUucHJvdG90eXBlLl9hcHBlbmROb2RlPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMuX3RhaWw7bnVsbCE9PXQmJih0Lm5leHQ9ZSksZS5wcmV2PXQsZS5uZXh0PW51bGwsdGhpcy5fdGFpbD1lLG51bGw9PT10aGlzLl9oZWFkJiYodGhpcy5faGVhZD1lKX0sZS5wcm90b3R5cGUucHJlYWxsb2M9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PXRoaXMuX25vZGVQb29sLHI9MDtyPGU7cisrKXQucHVzaCh7cHJldjpudWxsLG5leHQ6bnVsbCxrZXk6bnVsbCx2YWx1ZTpudWxsfSl9LGUucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9tYXBbZV07cmV0dXJuIHZvaWQgMCE9PXQ/KHRoaXMuX3VubGlua05vZGUodCksdGhpcy5fYXBwZW5kTm9kZSh0KSx0LnZhbHVlKTpudWxsfSxlLnByb3RvdHlwZS5wZWVrVmFsdWU9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fbWFwW2VdO3JldHVybiB2b2lkIDAhPT10P3QudmFsdWU6bnVsbH0sZS5wcm90b3R5cGUucGVlaz1mdW5jdGlvbigpe3ZhciBlPXRoaXMuX2hlYWQ7cmV0dXJuIG51bGw9PT1lP251bGw6ZS52YWx1ZX0sZS5wcm90b3R5cGUuc2V0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5fbWFwW2VdO2lmKHZvaWQgMCE9PXIpcj10aGlzLl9tYXBbZV0sdGhpcy5fdW5saW5rTm9kZShyKSxyLnZhbHVlPXQ7ZWxzZSBpZih0aGlzLnNpemU+PXRoaXMuY2FwYWNpdHkpcj10aGlzLl9oZWFkLHRoaXMuX3VubGlua05vZGUociksZGVsZXRlIHRoaXMuX21hcFtyLmtleV0sci5rZXk9ZSxyLnZhbHVlPXQsdGhpcy5fbWFwW2VdPXI7ZWxzZXt2YXIgaT10aGlzLl9ub2RlUG9vbDtpLmxlbmd0aD4wPygocj1pLnBvcCgpKS5rZXk9ZSxyLnZhbHVlPXQpOnI9e3ByZXY6bnVsbCxuZXh0Om51bGwsa2V5OmUsdmFsdWU6dH0sdGhpcy5fbWFwW2VdPXIsdGhpcy5zaXplKyt9dGhpcy5fYXBwZW5kTm9kZShyKX0sZX0oKTt0LkxSVU1hcD1yfSwxMjk2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkRvbVJlbmRlcmVyPXZvaWQgMDt2YXIgYT1yKDM3ODcpLGM9cig4ODAzKSxsPXIoODQ0KSx1PXIoNDcyNSksaD1yKDI1ODUpLGY9cig4NDYwKSxfPXIoNDc3NCksZD1yKDk2MzEpLHA9Inh0ZXJtLWRvbS1yZW5kZXJlci1vd25lci0iLHY9Inh0ZXJtLWZnLSIsZz0ieHRlcm0tYmctIix5PSJ4dGVybS1mb2N1cyIsbT0xLGI9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8scyxjLGwsdSxoKXt2YXIgZj1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIGYuX2NvbG9ycz10LGYuX2VsZW1lbnQ9cixmLl9zY3JlZW5FbGVtZW50PWksZi5fdmlld3BvcnRFbGVtZW50PW4sZi5fbGlua2lmaWVyPW8sZi5fbGlua2lmaWVyMj1zLGYuX2NoYXJTaXplU2VydmljZT1sLGYuX29wdGlvbnNTZXJ2aWNlPXUsZi5fYnVmZmVyU2VydmljZT1oLGYuX3Rlcm1pbmFsQ2xhc3M9bSsrLGYuX3Jvd0VsZW1lbnRzPVtdLGYuX3Jvd0NvbnRhaW5lcj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJkaXYiKSxmLl9yb3dDb250YWluZXIuY2xhc3NMaXN0LmFkZCgieHRlcm0tcm93cyIpLGYuX3Jvd0NvbnRhaW5lci5zdHlsZS5saW5lSGVpZ2h0PSJub3JtYWwiLGYuX3Jvd0NvbnRhaW5lci5zZXRBdHRyaWJ1dGUoImFyaWEtaGlkZGVuIiwidHJ1ZSIpLGYuX3JlZnJlc2hSb3dFbGVtZW50cyhmLl9idWZmZXJTZXJ2aWNlLmNvbHMsZi5fYnVmZmVyU2VydmljZS5yb3dzKSxmLl9zZWxlY3Rpb25Db250YWluZXI9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2IiksZi5fc2VsZWN0aW9uQ29udGFpbmVyLmNsYXNzTGlzdC5hZGQoInh0ZXJtLXNlbGVjdGlvbiIpLGYuX3NlbGVjdGlvbkNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoImFyaWEtaGlkZGVuIiwidHJ1ZSIpLGYuZGltZW5zaW9ucz17c2NhbGVkQ2hhcldpZHRoOjAsc2NhbGVkQ2hhckhlaWdodDowLHNjYWxlZENlbGxXaWR0aDowLHNjYWxlZENlbGxIZWlnaHQ6MCxzY2FsZWRDaGFyTGVmdDowLHNjYWxlZENoYXJUb3A6MCxzY2FsZWRDYW52YXNXaWR0aDowLHNjYWxlZENhbnZhc0hlaWdodDowLGNhbnZhc1dpZHRoOjAsY2FudmFzSGVpZ2h0OjAsYWN0dWFsQ2VsbFdpZHRoOjAsYWN0dWFsQ2VsbEhlaWdodDowfSxmLl91cGRhdGVEaW1lbnNpb25zKCksZi5faW5qZWN0Q3NzKCksZi5fcm93RmFjdG9yeT1jLmNyZWF0ZUluc3RhbmNlKGEuRG9tUmVuZGVyZXJSb3dGYWN0b3J5LGRvY3VtZW50LGYuX2NvbG9ycyksZi5fZWxlbWVudC5jbGFzc0xpc3QuYWRkKHArZi5fdGVybWluYWxDbGFzcyksZi5fc2NyZWVuRWxlbWVudC5hcHBlbmRDaGlsZChmLl9yb3dDb250YWluZXIpLGYuX3NjcmVlbkVsZW1lbnQuYXBwZW5kQ2hpbGQoZi5fc2VsZWN0aW9uQ29udGFpbmVyKSxmLl9saW5raWZpZXIub25TaG93TGlua1VuZGVybGluZSgoZnVuY3Rpb24oZSl7cmV0dXJuIGYuX29uTGlua0hvdmVyKGUpfSkpLGYuX2xpbmtpZmllci5vbkhpZGVMaW5rVW5kZXJsaW5lKChmdW5jdGlvbihlKXtyZXR1cm4gZi5fb25MaW5rTGVhdmUoZSl9KSksZi5fbGlua2lmaWVyMi5vblNob3dMaW5rVW5kZXJsaW5lKChmdW5jdGlvbihlKXtyZXR1cm4gZi5fb25MaW5rSG92ZXIoZSl9KSksZi5fbGlua2lmaWVyMi5vbkhpZGVMaW5rVW5kZXJsaW5lKChmdW5jdGlvbihlKXtyZXR1cm4gZi5fb25MaW5rTGVhdmUoZSl9KSksZn1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0UmVkcmF3Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuKG5ldyBmLkV2ZW50RW1pdHRlcikuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3RoaXMuX2VsZW1lbnQuY2xhc3NMaXN0LnJlbW92ZShwK3RoaXMuX3Rlcm1pbmFsQ2xhc3MpLCgwLGQucmVtb3ZlRWxlbWVudEZyb21QYXJlbnQpKHRoaXMuX3Jvd0NvbnRhaW5lcix0aGlzLl9zZWxlY3Rpb25Db250YWluZXIsdGhpcy5fdGhlbWVTdHlsZUVsZW1lbnQsdGhpcy5fZGltZW5zaW9uc1N0eWxlRWxlbWVudCksZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpfSx0LnByb3RvdHlwZS5fdXBkYXRlRGltZW5zaW9ucz1mdW5jdGlvbigpe3RoaXMuZGltZW5zaW9ucy5zY2FsZWRDaGFyV2lkdGg9dGhpcy5fY2hhclNpemVTZXJ2aWNlLndpZHRoKndpbmRvdy5kZXZpY2VQaXhlbFJhdGlvLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDaGFySGVpZ2h0PU1hdGguY2VpbCh0aGlzLl9jaGFyU2l6ZVNlcnZpY2UuaGVpZ2h0KndpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKSx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2VsbFdpZHRoPXRoaXMuZGltZW5zaW9ucy5zY2FsZWRDaGFyV2lkdGgrTWF0aC5yb3VuZCh0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmxldHRlclNwYWNpbmcpLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsSGVpZ2h0PU1hdGguZmxvb3IodGhpcy5kaW1lbnNpb25zLnNjYWxlZENoYXJIZWlnaHQqdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5saW5lSGVpZ2h0KSx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2hhckxlZnQ9MCx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2hhclRvcD0wLHRoaXMuZGltZW5zaW9ucy5zY2FsZWRDYW52YXNXaWR0aD10aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2VsbFdpZHRoKnRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2FudmFzSGVpZ2h0PXRoaXMuZGltZW5zaW9ucy5zY2FsZWRDZWxsSGVpZ2h0KnRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyx0aGlzLmRpbWVuc2lvbnMuY2FudmFzV2lkdGg9TWF0aC5yb3VuZCh0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2FudmFzV2lkdGgvd2luZG93LmRldmljZVBpeGVsUmF0aW8pLHRoaXMuZGltZW5zaW9ucy5jYW52YXNIZWlnaHQ9TWF0aC5yb3VuZCh0aGlzLmRpbWVuc2lvbnMuc2NhbGVkQ2FudmFzSGVpZ2h0L3dpbmRvdy5kZXZpY2VQaXhlbFJhdGlvKSx0aGlzLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoPXRoaXMuZGltZW5zaW9ucy5jYW52YXNXaWR0aC90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5kaW1lbnNpb25zLmFjdHVhbENlbGxIZWlnaHQ9dGhpcy5kaW1lbnNpb25zLmNhbnZhc0hlaWdodC90aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3M7Zm9yKHZhciBlPTAsdD10aGlzLl9yb3dFbGVtZW50cztlPHQubGVuZ3RoO2UrKyl7dmFyIHI9dFtlXTtyLnN0eWxlLndpZHRoPXRoaXMuZGltZW5zaW9ucy5jYW52YXNXaWR0aCsicHgiLHIuc3R5bGUuaGVpZ2h0PXRoaXMuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KyJweCIsci5zdHlsZS5saW5lSGVpZ2h0PXRoaXMuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KyJweCIsci5zdHlsZS5vdmVyZmxvdz0iaGlkZGVuIn10aGlzLl9kaW1lbnNpb25zU3R5bGVFbGVtZW50fHwodGhpcy5fZGltZW5zaW9uc1N0eWxlRWxlbWVudD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzdHlsZSIpLHRoaXMuX3NjcmVlbkVsZW1lbnQuYXBwZW5kQ2hpbGQodGhpcy5fZGltZW5zaW9uc1N0eWxlRWxlbWVudCkpO3ZhciBpPXRoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cyBzcGFuIHsgZGlzcGxheTogaW5saW5lLWJsb2NrOyBoZWlnaHQ6IDEwMCU7IHZlcnRpY2FsLWFsaWduOiB0b3A7IHdpZHRoOiAiK3RoaXMuZGltZW5zaW9ucy5hY3R1YWxDZWxsV2lkdGgrInB4fSI7dGhpcy5fZGltZW5zaW9uc1N0eWxlRWxlbWVudC50ZXh0Q29udGVudD1pLHRoaXMuX3NlbGVjdGlvbkNvbnRhaW5lci5zdHlsZS5oZWlnaHQ9dGhpcy5fdmlld3BvcnRFbGVtZW50LnN0eWxlLmhlaWdodCx0aGlzLl9zY3JlZW5FbGVtZW50LnN0eWxlLndpZHRoPXRoaXMuZGltZW5zaW9ucy5jYW52YXNXaWR0aCsicHgiLHRoaXMuX3NjcmVlbkVsZW1lbnQuc3R5bGUuaGVpZ2h0PXRoaXMuZGltZW5zaW9ucy5jYW52YXNIZWlnaHQrInB4In0sdC5wcm90b3R5cGUuc2V0Q29sb3JzPWZ1bmN0aW9uKGUpe3RoaXMuX2NvbG9ycz1lLHRoaXMuX2luamVjdENzcygpfSx0LnByb3RvdHlwZS5faW5qZWN0Q3NzPWZ1bmN0aW9uKCl7dmFyIGU9dGhpczt0aGlzLl90aGVtZVN0eWxlRWxlbWVudHx8KHRoaXMuX3RoZW1lU3R5bGVFbGVtZW50PWRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoInN0eWxlIiksdGhpcy5fc2NyZWVuRWxlbWVudC5hcHBlbmRDaGlsZCh0aGlzLl90aGVtZVN0eWxlRWxlbWVudCkpO3ZhciB0PXRoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cyB7IGNvbG9yOiAiK3RoaXMuX2NvbG9ycy5mb3JlZ3JvdW5kLmNzcysiOyBmb250LWZhbWlseTogIit0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZvbnRGYW1pbHkrIjsgZm9udC1zaXplOiAiK3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZm9udFNpemUrInB4O30iO3QrPXRoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiBzcGFuOm5vdCguIithLkJPTERfQ0xBU1MrIikgeyBmb250LXdlaWdodDogIit0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmZvbnRXZWlnaHQrIjt9Iit0aGlzLl90ZXJtaW5hbFNlbGVjdG9yKyIgc3Bhbi4iK2EuQk9MRF9DTEFTUysiIHsgZm9udC13ZWlnaHQ6ICIrdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5mb250V2VpZ2h0Qm9sZCsiO30iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiBzcGFuLiIrYS5JVEFMSUNfQ0xBU1MrIiB7IGZvbnQtc3R5bGU6IGl0YWxpYzt9Iix0Kz0iQGtleWZyYW1lcyBibGlua19ib3hfc2hhZG93XyIrdGhpcy5fdGVybWluYWxDbGFzcysiIHsgNTAlIHsgIGJveC1zaGFkb3c6IG5vbmU7IH19Iix0Kz0iQGtleWZyYW1lcyBibGlua19ibG9ja18iK3RoaXMuX3Rlcm1pbmFsQ2xhc3MrIiB7IDAlIHsgIGJhY2tncm91bmQtY29sb3I6ICIrdGhpcy5fY29sb3JzLmN1cnNvci5jc3MrIjsgIGNvbG9yOiAiK3RoaXMuX2NvbG9ycy5jdXJzb3JBY2NlbnQuY3NzKyI7IH0gNTAlIHsgIGJhY2tncm91bmQtY29sb3I6ICIrdGhpcy5fY29sb3JzLmN1cnNvckFjY2VudC5jc3MrIjsgIGNvbG9yOiAiK3RoaXMuX2NvbG9ycy5jdXJzb3IuY3NzKyI7IH19Iix0Kz10aGlzLl90ZXJtaW5hbFNlbGVjdG9yKyIgLnh0ZXJtLXJvd3M6bm90KC54dGVybS1mb2N1cykgLiIrYS5DVVJTT1JfQ0xBU1MrIi4iK2EuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTKyIgeyBvdXRsaW5lOiAxcHggc29saWQgIit0aGlzLl9jb2xvcnMuY3Vyc29yLmNzcysiOyBvdXRsaW5lLW9mZnNldDogLTFweDt9Iit0aGlzLl90ZXJtaW5hbFNlbGVjdG9yKyIgLnh0ZXJtLXJvd3MueHRlcm0tZm9jdXMgLiIrYS5DVVJTT1JfQ0xBU1MrIi4iK2EuQ1VSU09SX0JMSU5LX0NMQVNTKyI6bm90KC4iK2EuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTKyIpIHsgYW5pbWF0aW9uOiBibGlua19ib3hfc2hhZG93XyIrdGhpcy5fdGVybWluYWxDbGFzcysiIDFzIHN0ZXAtZW5kIGluZmluaXRlO30iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cy54dGVybS1mb2N1cyAuIithLkNVUlNPUl9DTEFTUysiLiIrYS5DVVJTT1JfQkxJTktfQ0xBU1MrIi4iK2EuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTKyIgeyBhbmltYXRpb246IGJsaW5rX2Jsb2NrXyIrdGhpcy5fdGVybWluYWxDbGFzcysiIDFzIHN0ZXAtZW5kIGluZmluaXRlO30iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cy54dGVybS1mb2N1cyAuIithLkNVUlNPUl9DTEFTUysiLiIrYS5DVVJTT1JfU1RZTEVfQkxPQ0tfQ0xBU1MrIiB7IGJhY2tncm91bmQtY29sb3I6ICIrdGhpcy5fY29sb3JzLmN1cnNvci5jc3MrIjsgY29sb3I6ICIrdGhpcy5fY29sb3JzLmN1cnNvckFjY2VudC5jc3MrIjt9Iit0aGlzLl90ZXJtaW5hbFNlbGVjdG9yKyIgLnh0ZXJtLXJvd3MgLiIrYS5DVVJTT1JfQ0xBU1MrIi4iK2EuQ1VSU09SX1NUWUxFX0JBUl9DTEFTUysiIHsgYm94LXNoYWRvdzogIit0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmN1cnNvcldpZHRoKyJweCAwIDAgIit0aGlzLl9jb2xvcnMuY3Vyc29yLmNzcysiIGluc2V0O30iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAueHRlcm0tcm93cyAuIithLkNVUlNPUl9DTEFTUysiLiIrYS5DVVJTT1JfU1RZTEVfVU5ERVJMSU5FX0NMQVNTKyIgeyBib3gtc2hhZG93OiAwIC0xcHggMCAiK3RoaXMuX2NvbG9ycy5jdXJzb3IuY3NzKyIgaW5zZXQ7fSIsdCs9dGhpcy5fdGVybWluYWxTZWxlY3RvcisiIC54dGVybS1zZWxlY3Rpb24geyBwb3NpdGlvbjogYWJzb2x1dGU7IHRvcDogMDsgbGVmdDogMDsgei1pbmRleDogMTsgcG9pbnRlci1ldmVudHM6IG5vbmU7fSIrdGhpcy5fdGVybWluYWxTZWxlY3RvcisiIC54dGVybS1zZWxlY3Rpb24gZGl2IHsgcG9zaXRpb246IGFic29sdXRlOyBiYWNrZ3JvdW5kLWNvbG9yOiAiK3RoaXMuX2NvbG9ycy5zZWxlY3Rpb25UcmFuc3BhcmVudC5jc3MrIjt9Iix0aGlzLl9jb2xvcnMuYW5zaS5mb3JFYWNoKChmdW5jdGlvbihyLGkpe3QrPWUuX3Rlcm1pbmFsU2VsZWN0b3IrIiAuIit2K2krIiB7IGNvbG9yOiAiK3IuY3NzKyI7IH0iK2UuX3Rlcm1pbmFsU2VsZWN0b3IrIiAuIitnK2krIiB7IGJhY2tncm91bmQtY29sb3I6ICIrci5jc3MrIjsgfSJ9KSksdCs9dGhpcy5fdGVybWluYWxTZWxlY3RvcisiIC4iK3YrYy5JTlZFUlRFRF9ERUZBVUxUX0NPTE9SKyIgeyBjb2xvcjogIitfLmNvbG9yLm9wYXF1ZSh0aGlzLl9jb2xvcnMuYmFja2dyb3VuZCkuY3NzKyI7IH0iK3RoaXMuX3Rlcm1pbmFsU2VsZWN0b3IrIiAuIitnK2MuSU5WRVJURURfREVGQVVMVF9DT0xPUisiIHsgYmFja2dyb3VuZC1jb2xvcjogIit0aGlzLl9jb2xvcnMuZm9yZWdyb3VuZC5jc3MrIjsgfSIsdGhpcy5fdGhlbWVTdHlsZUVsZW1lbnQudGV4dENvbnRlbnQ9dH0sdC5wcm90b3R5cGUub25EZXZpY2VQaXhlbFJhdGlvQ2hhbmdlPWZ1bmN0aW9uKCl7dGhpcy5fdXBkYXRlRGltZW5zaW9ucygpfSx0LnByb3RvdHlwZS5fcmVmcmVzaFJvd0VsZW1lbnRzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRoaXMuX3Jvd0VsZW1lbnRzLmxlbmd0aDtyPD10O3IrKyl7dmFyIGk9ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iik7dGhpcy5fcm93Q29udGFpbmVyLmFwcGVuZENoaWxkKGkpLHRoaXMuX3Jvd0VsZW1lbnRzLnB1c2goaSl9Zm9yKDt0aGlzLl9yb3dFbGVtZW50cy5sZW5ndGg+dDspdGhpcy5fcm93Q29udGFpbmVyLnJlbW92ZUNoaWxkKHRoaXMuX3Jvd0VsZW1lbnRzLnBvcCgpKX0sdC5wcm90b3R5cGUub25SZXNpemU9ZnVuY3Rpb24oZSx0KXt0aGlzLl9yZWZyZXNoUm93RWxlbWVudHMoZSx0KSx0aGlzLl91cGRhdGVEaW1lbnNpb25zKCl9LHQucHJvdG90eXBlLm9uQ2hhclNpemVDaGFuZ2VkPWZ1bmN0aW9uKCl7dGhpcy5fdXBkYXRlRGltZW5zaW9ucygpfSx0LnByb3RvdHlwZS5vbkJsdXI9ZnVuY3Rpb24oKXt0aGlzLl9yb3dDb250YWluZXIuY2xhc3NMaXN0LnJlbW92ZSh5KX0sdC5wcm90b3R5cGUub25Gb2N1cz1mdW5jdGlvbigpe3RoaXMuX3Jvd0NvbnRhaW5lci5jbGFzc0xpc3QuYWRkKHkpfSx0LnByb3RvdHlwZS5vblNlbGVjdGlvbkNoYW5nZWQ9ZnVuY3Rpb24oZSx0LHIpe2Zvcig7dGhpcy5fc2VsZWN0aW9uQ29udGFpbmVyLmNoaWxkcmVuLmxlbmd0aDspdGhpcy5fc2VsZWN0aW9uQ29udGFpbmVyLnJlbW92ZUNoaWxkKHRoaXMuX3NlbGVjdGlvbkNvbnRhaW5lci5jaGlsZHJlblswXSk7aWYoZSYmdCl7dmFyIGk9ZVsxXS10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCxuPXRbMV0tdGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3Asbz1NYXRoLm1heChpLDApLHM9TWF0aC5taW4obix0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSk7aWYoIShvPj10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3N8fHM8MCkpe3ZhciBhPWRvY3VtZW50LmNyZWF0ZURvY3VtZW50RnJhZ21lbnQoKTtpZihyKWEuYXBwZW5kQ2hpbGQodGhpcy5fY3JlYXRlU2VsZWN0aW9uRWxlbWVudChvLGVbMF0sdFswXSxzLW8rMSkpO2Vsc2V7dmFyIGM9aT09PW8/ZVswXTowLGw9bz09PW4/dFswXTp0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM7YS5hcHBlbmRDaGlsZCh0aGlzLl9jcmVhdGVTZWxlY3Rpb25FbGVtZW50KG8sYyxsKSk7dmFyIHU9cy1vLTE7aWYoYS5hcHBlbmRDaGlsZCh0aGlzLl9jcmVhdGVTZWxlY3Rpb25FbGVtZW50KG8rMSwwLHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx1KSksbyE9PXMpe3ZhciBoPW49PT1zP3RbMF06dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzO2EuYXBwZW5kQ2hpbGQodGhpcy5fY3JlYXRlU2VsZWN0aW9uRWxlbWVudChzLDAsaCkpfX10aGlzLl9zZWxlY3Rpb25Db250YWluZXIuYXBwZW5kQ2hpbGQoYSl9fX0sdC5wcm90b3R5cGUuX2NyZWF0ZVNlbGVjdGlvbkVsZW1lbnQ9ZnVuY3Rpb24oZSx0LHIsaSl7dm9pZCAwPT09aSYmKGk9MSk7dmFyIG49ZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgiZGl2Iik7cmV0dXJuIG4uc3R5bGUuaGVpZ2h0PWkqdGhpcy5kaW1lbnNpb25zLmFjdHVhbENlbGxIZWlnaHQrInB4IixuLnN0eWxlLnRvcD1lKnRoaXMuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0KyJweCIsbi5zdHlsZS5sZWZ0PXQqdGhpcy5kaW1lbnNpb25zLmFjdHVhbENlbGxXaWR0aCsicHgiLG4uc3R5bGUud2lkdGg9dGhpcy5kaW1lbnNpb25zLmFjdHVhbENlbGxXaWR0aCooci10KSsicHgiLG59LHQucHJvdG90eXBlLm9uQ3Vyc29yTW92ZT1mdW5jdGlvbigpe30sdC5wcm90b3R5cGUub25PcHRpb25zQ2hhbmdlZD1mdW5jdGlvbigpe3RoaXMuX3VwZGF0ZURpbWVuc2lvbnMoKSx0aGlzLl9pbmplY3RDc3MoKX0sdC5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXtmb3IodmFyIGU9MCx0PXRoaXMuX3Jvd0VsZW1lbnRzO2U8dC5sZW5ndGg7ZSsrKXRbZV0uaW5uZXJUZXh0PSIifSx0LnByb3RvdHlwZS5yZW5kZXJSb3dzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnliYXNlK3RoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnksaT1NYXRoLm1pbih0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci54LHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scy0xKSxuPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yQmxpbmssbz1lO288PXQ7bysrKXt2YXIgcz10aGlzLl9yb3dFbGVtZW50c1tvXTtzLmlubmVyVGV4dD0iIjt2YXIgYT1vK3RoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwLGM9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KGEpLGw9dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JTdHlsZTtzLmFwcGVuZENoaWxkKHRoaXMuX3Jvd0ZhY3RvcnkuY3JlYXRlUm93KGMsYSxhPT09cixsLGksbix0aGlzLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoLHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scykpfX0sT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJfdGVybWluYWxTZWxlY3RvciIse2dldDpmdW5jdGlvbigpe3JldHVybiIuIitwK3RoaXMuX3Rlcm1pbmFsQ2xhc3N9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuX29uTGlua0hvdmVyPWZ1bmN0aW9uKGUpe3RoaXMuX3NldENlbGxVbmRlcmxpbmUoZS54MSxlLngyLGUueTEsZS55MixlLmNvbHMsITApfSx0LnByb3RvdHlwZS5fb25MaW5rTGVhdmU9ZnVuY3Rpb24oZSl7dGhpcy5fc2V0Q2VsbFVuZGVybGluZShlLngxLGUueDIsZS55MSxlLnkyLGUuY29scywhMSl9LHQucHJvdG90eXBlLl9zZXRDZWxsVW5kZXJsaW5lPWZ1bmN0aW9uKGUsdCxyLGksbixvKXtmb3IoO2UhPT10fHxyIT09aTspe3ZhciBzPXRoaXMuX3Jvd0VsZW1lbnRzW3JdO2lmKCFzKXJldHVybjt2YXIgYT1zLmNoaWxkcmVuW2VdO2EmJihhLnN0eWxlLnRleHREZWNvcmF0aW9uPW8/InVuZGVybGluZSI6Im5vbmUiKSwrK2U+PW4mJihlPTAscisrKX19LG8oW3MoNixoLklJbnN0YW50aWF0aW9uU2VydmljZSkscyg3LHUuSUNoYXJTaXplU2VydmljZSkscyg4LGguSU9wdGlvbnNTZXJ2aWNlKSxzKDksaC5JQnVmZmVyU2VydmljZSldLHQpfShsLkRpc3Bvc2FibGUpO3QuRG9tUmVuZGVyZXI9Yn0sMzc4NzpmdW5jdGlvbihlLHQscil7dmFyIGk9dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxuPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkRvbVJlbmRlcmVyUm93RmFjdG9yeT10LkNVUlNPUl9TVFlMRV9VTkRFUkxJTkVfQ0xBU1M9dC5DVVJTT1JfU1RZTEVfQkFSX0NMQVNTPXQuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTPXQuQ1VSU09SX0JMSU5LX0NMQVNTPXQuQ1VSU09SX0NMQVNTPXQuU1RSSUtFVEhST1VHSF9DTEFTUz10LlVOREVSTElORV9DTEFTUz10LklUQUxJQ19DTEFTUz10LkRJTV9DTEFTUz10LkJPTERfQ0xBU1M9dm9pZCAwO3ZhciBvPXIoODgwMykscz1yKDY0MyksYT1yKDUxMSksYz1yKDI1ODUpLGw9cig0Nzc0KSx1PXIoNDcyNSksaD1yKDQyNjkpO3QuQk9MRF9DTEFTUz0ieHRlcm0tYm9sZCIsdC5ESU1fQ0xBU1M9Inh0ZXJtLWRpbSIsdC5JVEFMSUNfQ0xBU1M9Inh0ZXJtLWl0YWxpYyIsdC5VTkRFUkxJTkVfQ0xBU1M9Inh0ZXJtLXVuZGVybGluZSIsdC5TVFJJS0VUSFJPVUdIX0NMQVNTPSJ4dGVybS1zdHJpa2V0aHJvdWdoIix0LkNVUlNPUl9DTEFTUz0ieHRlcm0tY3Vyc29yIix0LkNVUlNPUl9CTElOS19DTEFTUz0ieHRlcm0tY3Vyc29yLWJsaW5rIix0LkNVUlNPUl9TVFlMRV9CTE9DS19DTEFTUz0ieHRlcm0tY3Vyc29yLWJsb2NrIix0LkNVUlNPUl9TVFlMRV9CQVJfQ0xBU1M9Inh0ZXJtLWN1cnNvci1iYXIiLHQuQ1VSU09SX1NUWUxFX1VOREVSTElORV9DTEFTUz0ieHRlcm0tY3Vyc29yLXVuZGVybGluZSI7dmFyIGY9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyLGksbil7dGhpcy5fZG9jdW1lbnQ9ZSx0aGlzLl9jb2xvcnM9dCx0aGlzLl9jaGFyYWN0ZXJKb2luZXJTZXJ2aWNlPXIsdGhpcy5fb3B0aW9uc1NlcnZpY2U9aSx0aGlzLl9jb3JlU2VydmljZT1uLHRoaXMuX3dvcmtDZWxsPW5ldyBhLkNlbGxEYXRhfXJldHVybiBlLnByb3RvdHlwZS5zZXRDb2xvcnM9ZnVuY3Rpb24oZSl7dGhpcy5fY29sb3JzPWV9LGUucHJvdG90eXBlLmNyZWF0ZVJvdz1mdW5jdGlvbihlLHIsaSxuLGEsYyx1LGYpe2Zvcih2YXIgZD10aGlzLl9kb2N1bWVudC5jcmVhdGVEb2N1bWVudEZyYWdtZW50KCkscD10aGlzLl9jaGFyYWN0ZXJKb2luZXJTZXJ2aWNlLmdldEpvaW5lZENoYXJhY3RlcnMociksdj0wLGc9TWF0aC5taW4oZS5sZW5ndGgsZiktMTtnPj0wO2ctLSlpZihlLmxvYWRDZWxsKGcsdGhpcy5fd29ya0NlbGwpLmdldENvZGUoKSE9PXMuTlVMTF9DRUxMX0NPREV8fGkmJmc9PT1hKXt2PWcrMTticmVha31mb3IoZz0wO2c8djtnKyspe2UubG9hZENlbGwoZyx0aGlzLl93b3JrQ2VsbCk7dmFyIHk9dGhpcy5fd29ya0NlbGwuZ2V0V2lkdGgoKTtpZigwIT09eSl7dmFyIG09ITEsYj1nLFM9dGhpcy5fd29ya0NlbGw7aWYocC5sZW5ndGg+MCYmZz09PXBbMF1bMF0pe209ITA7dmFyIEM9cC5zaGlmdCgpO1M9bmV3IGguSm9pbmVkQ2VsbERhdGEodGhpcy5fd29ya0NlbGwsZS50cmFuc2xhdGVUb1N0cmluZyghMCxDWzBdLENbMV0pLENbMV0tQ1swXSksYj1DWzFdLTEseT1TLmdldFdpZHRoKCl9dmFyIHc9dGhpcy5fZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgic3BhbiIpO2lmKHk+MSYmKHcuc3R5bGUud2lkdGg9dSp5KyJweCIpLG0mJih3LnN0eWxlLmRpc3BsYXk9ImlubGluZSIsYT49ZyYmYTw9YiYmKGE9ZykpLCF0aGlzLl9jb3JlU2VydmljZS5pc0N1cnNvckhpZGRlbiYmaSYmZz09PWEpc3dpdGNoKHcuY2xhc3NMaXN0LmFkZCh0LkNVUlNPUl9DTEFTUyksYyYmdy5jbGFzc0xpc3QuYWRkKHQuQ1VSU09SX0JMSU5LX0NMQVNTKSxuKXtjYXNlImJhciI6dy5jbGFzc0xpc3QuYWRkKHQuQ1VSU09SX1NUWUxFX0JBUl9DTEFTUyk7YnJlYWs7Y2FzZSJ1bmRlcmxpbmUiOncuY2xhc3NMaXN0LmFkZCh0LkNVUlNPUl9TVFlMRV9VTkRFUkxJTkVfQ0xBU1MpO2JyZWFrO2RlZmF1bHQ6dy5jbGFzc0xpc3QuYWRkKHQuQ1VSU09SX1NUWUxFX0JMT0NLX0NMQVNTKX1TLmlzQm9sZCgpJiZ3LmNsYXNzTGlzdC5hZGQodC5CT0xEX0NMQVNTKSxTLmlzSXRhbGljKCkmJncuY2xhc3NMaXN0LmFkZCh0LklUQUxJQ19DTEFTUyksUy5pc0RpbSgpJiZ3LmNsYXNzTGlzdC5hZGQodC5ESU1fQ0xBU1MpLFMuaXNVbmRlcmxpbmUoKSYmdy5jbGFzc0xpc3QuYWRkKHQuVU5ERVJMSU5FX0NMQVNTKSxTLmlzSW52aXNpYmxlKCk/dy50ZXh0Q29udGVudD1zLldISVRFU1BBQ0VfQ0VMTF9DSEFSOncudGV4dENvbnRlbnQ9Uy5nZXRDaGFycygpfHxzLldISVRFU1BBQ0VfQ0VMTF9DSEFSLFMuaXNTdHJpa2V0aHJvdWdoKCkmJncuY2xhc3NMaXN0LmFkZCh0LlNUUklLRVRIUk9VR0hfQ0xBU1MpO3ZhciBMPVMuZ2V0RmdDb2xvcigpLEU9Uy5nZXRGZ0NvbG9yTW9kZSgpLHg9Uy5nZXRCZ0NvbG9yKCksQT1TLmdldEJnQ29sb3JNb2RlKCksaz0hIVMuaXNJbnZlcnNlKCk7aWYoayl7dmFyIE09TDtMPXgseD1NO3ZhciBSPUU7RT1BLEE9Un1zd2l0Y2goRSl7Y2FzZSAxNjc3NzIxNjpjYXNlIDMzNTU0NDMyOlMuaXNCb2xkKCkmJkw8OCYmdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5kcmF3Qm9sZFRleHRJbkJyaWdodENvbG9ycyYmKEwrPTgpLHRoaXMuX2FwcGx5TWluaW11bUNvbnRyYXN0KHcsdGhpcy5fY29sb3JzLmJhY2tncm91bmQsdGhpcy5fY29sb3JzLmFuc2lbTF0pfHx3LmNsYXNzTGlzdC5hZGQoInh0ZXJtLWZnLSIrTCk7YnJlYWs7Y2FzZSA1MDMzMTY0ODp2YXIgVD1sLnJnYmEudG9Db2xvcihMPj4xNiYyNTUsTD4+OCYyNTUsMjU1JkwpO3RoaXMuX2FwcGx5TWluaW11bUNvbnRyYXN0KHcsdGhpcy5fY29sb3JzLmJhY2tncm91bmQsVCl8fHRoaXMuX2FkZFN0eWxlKHcsImNvbG9yOiMiK18oTC50b1N0cmluZygxNiksIjAiLDYpKTticmVhaztkZWZhdWx0OnRoaXMuX2FwcGx5TWluaW11bUNvbnRyYXN0KHcsdGhpcy5fY29sb3JzLmJhY2tncm91bmQsdGhpcy5fY29sb3JzLmZvcmVncm91bmQpfHxrJiZ3LmNsYXNzTGlzdC5hZGQoInh0ZXJtLWZnLSIrby5JTlZFUlRFRF9ERUZBVUxUX0NPTE9SKX1zd2l0Y2goQSl7Y2FzZSAxNjc3NzIxNjpjYXNlIDMzNTU0NDMyOncuY2xhc3NMaXN0LmFkZCgieHRlcm0tYmctIit4KTticmVhaztjYXNlIDUwMzMxNjQ4OnRoaXMuX2FkZFN0eWxlKHcsImJhY2tncm91bmQtY29sb3I6IyIrXyh4LnRvU3RyaW5nKDE2KSwiMCIsNikpO2JyZWFrO2RlZmF1bHQ6ayYmdy5jbGFzc0xpc3QuYWRkKCJ4dGVybS1iZy0iK28uSU5WRVJURURfREVGQVVMVF9DT0xPUil9ZC5hcHBlbmRDaGlsZCh3KSxnPWJ9fXJldHVybiBkfSxlLnByb3RvdHlwZS5fYXBwbHlNaW5pbXVtQ29udHJhc3Q9ZnVuY3Rpb24oZSx0LHIpe2lmKDE9PT10aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLm1pbmltdW1Db250cmFzdFJhdGlvKXJldHVybiExO3ZhciBpPXRoaXMuX2NvbG9ycy5jb250cmFzdENhY2hlLmdldENvbG9yKHRoaXMuX3dvcmtDZWxsLmJnLHRoaXMuX3dvcmtDZWxsLmZnKTtyZXR1cm4gdm9pZCAwPT09aSYmKGk9bC5jb2xvci5lbnN1cmVDb250cmFzdFJhdGlvKHQscix0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLm1pbmltdW1Db250cmFzdFJhdGlvKSx0aGlzLl9jb2xvcnMuY29udHJhc3RDYWNoZS5zZXRDb2xvcih0aGlzLl93b3JrQ2VsbC5iZyx0aGlzLl93b3JrQ2VsbC5mZyxudWxsIT1pP2k6bnVsbCkpLCEhaSYmKHRoaXMuX2FkZFN0eWxlKGUsImNvbG9yOiIraS5jc3MpLCEwKX0sZS5wcm90b3R5cGUuX2FkZFN0eWxlPWZ1bmN0aW9uKGUsdCl7ZS5zZXRBdHRyaWJ1dGUoInN0eWxlIiwiIisoZS5nZXRBdHRyaWJ1dGUoInN0eWxlIil8fCIiKSt0KyI7Iil9LGkoW24oMix1LklDaGFyYWN0ZXJKb2luZXJTZXJ2aWNlKSxuKDMsYy5JT3B0aW9uc1NlcnZpY2UpLG4oNCxjLklDb3JlU2VydmljZSldLGUpfSgpO2Z1bmN0aW9uIF8oZSx0LHIpe2Zvcig7ZS5sZW5ndGg8cjspZT10K2U7cmV0dXJuIGV9dC5Eb21SZW5kZXJlclJvd0ZhY3Rvcnk9Zn0sNDU2OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuU2VsZWN0aW9uTW9kZWw9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9idWZmZXJTZXJ2aWNlPWUsdGhpcy5pc1NlbGVjdEFsbEFjdGl2ZT0hMSx0aGlzLnNlbGVjdGlvblN0YXJ0TGVuZ3RoPTB9cmV0dXJuIGUucHJvdG90eXBlLmNsZWFyU2VsZWN0aW9uPWZ1bmN0aW9uKCl7dGhpcy5zZWxlY3Rpb25TdGFydD12b2lkIDAsdGhpcy5zZWxlY3Rpb25FbmQ9dm9pZCAwLHRoaXMuaXNTZWxlY3RBbGxBY3RpdmU9ITEsdGhpcy5zZWxlY3Rpb25TdGFydExlbmd0aD0wfSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImZpbmFsU2VsZWN0aW9uU3RhcnQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pc1NlbGVjdEFsbEFjdGl2ZT9bMCwwXTp0aGlzLnNlbGVjdGlvbkVuZCYmdGhpcy5zZWxlY3Rpb25TdGFydCYmdGhpcy5hcmVTZWxlY3Rpb25WYWx1ZXNSZXZlcnNlZCgpP3RoaXMuc2VsZWN0aW9uRW5kOnRoaXMuc2VsZWN0aW9uU3RhcnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJmaW5hbFNlbGVjdGlvbkVuZCIse2dldDpmdW5jdGlvbigpe2lmKHRoaXMuaXNTZWxlY3RBbGxBY3RpdmUpcmV0dXJuW3RoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55YmFzZSt0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMV07aWYodGhpcy5zZWxlY3Rpb25TdGFydCl7aWYoIXRoaXMuc2VsZWN0aW9uRW5kfHx0aGlzLmFyZVNlbGVjdGlvblZhbHVlc1JldmVyc2VkKCkpe3ZhciBlPXRoaXMuc2VsZWN0aW9uU3RhcnRbMF0rdGhpcy5zZWxlY3Rpb25TdGFydExlbmd0aDtyZXR1cm4gZT50aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM/ZSV0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM9PTA/W3RoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLnNlbGVjdGlvblN0YXJ0WzFdK01hdGguZmxvb3IoZS90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpLTFdOltlJXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyx0aGlzLnNlbGVjdGlvblN0YXJ0WzFdK01hdGguZmxvb3IoZS90aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpXTpbZSx0aGlzLnNlbGVjdGlvblN0YXJ0WzFdXX1yZXR1cm4gdGhpcy5zZWxlY3Rpb25TdGFydExlbmd0aCYmdGhpcy5zZWxlY3Rpb25FbmRbMV09PT10aGlzLnNlbGVjdGlvblN0YXJ0WzFdP1tNYXRoLm1heCh0aGlzLnNlbGVjdGlvblN0YXJ0WzBdK3RoaXMuc2VsZWN0aW9uU3RhcnRMZW5ndGgsdGhpcy5zZWxlY3Rpb25FbmRbMF0pLHRoaXMuc2VsZWN0aW9uRW5kWzFdXTp0aGlzLnNlbGVjdGlvbkVuZH19LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUuYXJlU2VsZWN0aW9uVmFsdWVzUmV2ZXJzZWQ9ZnVuY3Rpb24oKXt2YXIgZT10aGlzLnNlbGVjdGlvblN0YXJ0LHQ9dGhpcy5zZWxlY3Rpb25FbmQ7cmV0dXJuISghZXx8IXQpJiYoZVsxXT50WzFdfHxlWzFdPT09dFsxXSYmZVswXT50WzBdKX0sZS5wcm90b3R5cGUub25UcmltPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLnNlbGVjdGlvblN0YXJ0JiYodGhpcy5zZWxlY3Rpb25TdGFydFsxXS09ZSksdGhpcy5zZWxlY3Rpb25FbmQmJih0aGlzLnNlbGVjdGlvbkVuZFsxXS09ZSksdGhpcy5zZWxlY3Rpb25FbmQmJnRoaXMuc2VsZWN0aW9uRW5kWzFdPDA/KHRoaXMuY2xlYXJTZWxlY3Rpb24oKSwhMCk6KHRoaXMuc2VsZWN0aW9uU3RhcnQmJnRoaXMuc2VsZWN0aW9uU3RhcnRbMV08MCYmKHRoaXMuc2VsZWN0aW9uU3RhcnRbMV09MCksITEpfSxlfSgpO3QuU2VsZWN0aW9uTW9kZWw9cn0sNDI4OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaT10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LG49dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQ2hhclNpemVTZXJ2aWNlPXZvaWQgMDt2YXIgbz1yKDI1ODUpLHM9cig4NDYwKSxhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQscil7dGhpcy5fb3B0aW9uc1NlcnZpY2U9cix0aGlzLndpZHRoPTAsdGhpcy5oZWlnaHQ9MCx0aGlzLl9vbkNoYXJTaXplQ2hhbmdlPW5ldyBzLkV2ZW50RW1pdHRlcix0aGlzLl9tZWFzdXJlU3RyYXRlZ3k9bmV3IGMoZSx0LHRoaXMuX29wdGlvbnNTZXJ2aWNlKX1yZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJoYXNWYWxpZFNpemUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy53aWR0aD4wJiZ0aGlzLmhlaWdodD4wfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25DaGFyU2l6ZUNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkNoYXJTaXplQ2hhbmdlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLm1lYXN1cmU9ZnVuY3Rpb24oKXt2YXIgZT10aGlzLl9tZWFzdXJlU3RyYXRlZ3kubWVhc3VyZSgpO2Uud2lkdGg9PT10aGlzLndpZHRoJiZlLmhlaWdodD09PXRoaXMuaGVpZ2h0fHwodGhpcy53aWR0aD1lLndpZHRoLHRoaXMuaGVpZ2h0PWUuaGVpZ2h0LHRoaXMuX29uQ2hhclNpemVDaGFuZ2UuZmlyZSgpKX0saShbbigyLG8uSU9wdGlvbnNTZXJ2aWNlKV0sZSl9KCk7dC5DaGFyU2l6ZVNlcnZpY2U9YTt2YXIgYz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSx0LHIpe3RoaXMuX2RvY3VtZW50PWUsdGhpcy5fcGFyZW50RWxlbWVudD10LHRoaXMuX29wdGlvbnNTZXJ2aWNlPXIsdGhpcy5fcmVzdWx0PXt3aWR0aDowLGhlaWdodDowfSx0aGlzLl9tZWFzdXJlRWxlbWVudD10aGlzLl9kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJzcGFuIiksdGhpcy5fbWVhc3VyZUVsZW1lbnQuY2xhc3NMaXN0LmFkZCgieHRlcm0tY2hhci1tZWFzdXJlLWVsZW1lbnQiKSx0aGlzLl9tZWFzdXJlRWxlbWVudC50ZXh0Q29udGVudD0iVyIsdGhpcy5fbWVhc3VyZUVsZW1lbnQuc2V0QXR0cmlidXRlKCJhcmlhLWhpZGRlbiIsInRydWUiKSx0aGlzLl9wYXJlbnRFbGVtZW50LmFwcGVuZENoaWxkKHRoaXMuX21lYXN1cmVFbGVtZW50KX1yZXR1cm4gZS5wcm90b3R5cGUubWVhc3VyZT1mdW5jdGlvbigpe3RoaXMuX21lYXN1cmVFbGVtZW50LnN0eWxlLmZvbnRGYW1pbHk9dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5mb250RmFtaWx5LHRoaXMuX21lYXN1cmVFbGVtZW50LnN0eWxlLmZvbnRTaXplPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZm9udFNpemUrInB4Ijt2YXIgZT10aGlzLl9tZWFzdXJlRWxlbWVudC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtyZXR1cm4gMCE9PWUud2lkdGgmJjAhPT1lLmhlaWdodCYmKHRoaXMuX3Jlc3VsdC53aWR0aD1lLndpZHRoLHRoaXMuX3Jlc3VsdC5oZWlnaHQ9TWF0aC5jZWlsKGUuaGVpZ2h0KSksdGhpcy5fcmVzdWx0fSxlfSgpfSw0MjY5OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkNoYXJhY3RlckpvaW5lclNlcnZpY2U9dC5Kb2luZWRDZWxsRGF0YT12b2lkIDA7dmFyIGE9cigzNzM0KSxjPXIoNjQzKSxsPXIoNTExKSx1PXIoMjU4NSksaD1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpKXt2YXIgbj1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIG4uY29udGVudD0wLG4uY29tYmluZWREYXRhPSIiLG4uZmc9dC5mZyxuLmJnPXQuYmcsbi5jb21iaW5lZERhdGE9cixuLl93aWR0aD1pLG59cmV0dXJuIG4odCxlKSx0LnByb3RvdHlwZS5pc0NvbWJpbmVkPWZ1bmN0aW9uKCl7cmV0dXJuIDIwOTcxNTJ9LHQucHJvdG90eXBlLmdldFdpZHRoPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX3dpZHRofSx0LnByb3RvdHlwZS5nZXRDaGFycz1mdW5jdGlvbigpe3JldHVybiB0aGlzLmNvbWJpbmVkRGF0YX0sdC5wcm90b3R5cGUuZ2V0Q29kZT1mdW5jdGlvbigpe3JldHVybiAyMDk3MTUxfSx0LnByb3RvdHlwZS5zZXRGcm9tQ2hhckRhdGE9ZnVuY3Rpb24oZSl7dGhyb3cgbmV3IEVycm9yKCJub3QgaW1wbGVtZW50ZWQiKX0sdC5wcm90b3R5cGUuZ2V0QXNDaGFyRGF0YT1mdW5jdGlvbigpe3JldHVyblt0aGlzLmZnLHRoaXMuZ2V0Q2hhcnMoKSx0aGlzLmdldFdpZHRoKCksdGhpcy5nZXRDb2RlKCldfSx0fShhLkF0dHJpYnV0ZURhdGEpO3QuSm9pbmVkQ2VsbERhdGE9aDt2YXIgZj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fYnVmZmVyU2VydmljZT1lLHRoaXMuX2NoYXJhY3RlckpvaW5lcnM9W10sdGhpcy5fbmV4dENoYXJhY3RlckpvaW5lcklkPTAsdGhpcy5fd29ya0NlbGw9bmV3IGwuQ2VsbERhdGF9cmV0dXJuIGUucHJvdG90eXBlLnJlZ2lzdGVyPWZ1bmN0aW9uKGUpe3ZhciB0PXtpZDp0aGlzLl9uZXh0Q2hhcmFjdGVySm9pbmVySWQrKyxoYW5kbGVyOmV9O3JldHVybiB0aGlzLl9jaGFyYWN0ZXJKb2luZXJzLnB1c2godCksdC5pZH0sZS5wcm90b3R5cGUuZGVyZWdpc3Rlcj1mdW5jdGlvbihlKXtmb3IodmFyIHQ9MDt0PHRoaXMuX2NoYXJhY3RlckpvaW5lcnMubGVuZ3RoO3QrKylpZih0aGlzLl9jaGFyYWN0ZXJKb2luZXJzW3RdLmlkPT09ZSlyZXR1cm4gdGhpcy5fY2hhcmFjdGVySm9pbmVycy5zcGxpY2UodCwxKSwhMDtyZXR1cm4hMX0sZS5wcm90b3R5cGUuZ2V0Sm9pbmVkQ2hhcmFjdGVycz1mdW5jdGlvbihlKXtpZigwPT09dGhpcy5fY2hhcmFjdGVySm9pbmVycy5sZW5ndGgpcmV0dXJuW107dmFyIHQ9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KGUpO2lmKCF0fHwwPT09dC5sZW5ndGgpcmV0dXJuW107Zm9yKHZhciByPVtdLGk9dC50cmFuc2xhdGVUb1N0cmluZyghMCksbj0wLG89MCxzPTAsYT10LmdldEZnKDApLGw9dC5nZXRCZygwKSx1PTA7dTx0LmdldFRyaW1tZWRMZW5ndGgoKTt1KyspaWYodC5sb2FkQ2VsbCh1LHRoaXMuX3dvcmtDZWxsKSwwIT09dGhpcy5fd29ya0NlbGwuZ2V0V2lkdGgoKSl7aWYodGhpcy5fd29ya0NlbGwuZmchPT1hfHx0aGlzLl93b3JrQ2VsbC5iZyE9PWwpe2lmKHUtbj4xKWZvcih2YXIgaD10aGlzLl9nZXRKb2luZWRSYW5nZXMoaSxzLG8sdCxuKSxmPTA7ZjxoLmxlbmd0aDtmKyspci5wdXNoKGhbZl0pO249dSxzPW8sYT10aGlzLl93b3JrQ2VsbC5mZyxsPXRoaXMuX3dvcmtDZWxsLmJnfW8rPXRoaXMuX3dvcmtDZWxsLmdldENoYXJzKCkubGVuZ3RofHxjLldISVRFU1BBQ0VfQ0VMTF9DSEFSLmxlbmd0aH1pZih0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtbj4xKWZvcihoPXRoaXMuX2dldEpvaW5lZFJhbmdlcyhpLHMsbyx0LG4pLGY9MDtmPGgubGVuZ3RoO2YrKylyLnB1c2goaFtmXSk7cmV0dXJuIHJ9LGUucHJvdG90eXBlLl9nZXRKb2luZWRSYW5nZXM9ZnVuY3Rpb24odCxyLGksbixvKXt2YXIgcz10LnN1YnN0cmluZyhyLGkpLGE9W107dHJ5e2E9dGhpcy5fY2hhcmFjdGVySm9pbmVyc1swXS5oYW5kbGVyKHMpfWNhdGNoKGUpe2NvbnNvbGUuZXJyb3IoZSl9Zm9yKHZhciBjPTE7Yzx0aGlzLl9jaGFyYWN0ZXJKb2luZXJzLmxlbmd0aDtjKyspdHJ5e2Zvcih2YXIgbD10aGlzLl9jaGFyYWN0ZXJKb2luZXJzW2NdLmhhbmRsZXIocyksdT0wO3U8bC5sZW5ndGg7dSsrKWUuX21lcmdlUmFuZ2VzKGEsbFt1XSl9Y2F0Y2goZSl7Y29uc29sZS5lcnJvcihlKX1yZXR1cm4gdGhpcy5fc3RyaW5nUmFuZ2VzVG9DZWxsUmFuZ2VzKGEsbixvKSxhfSxlLnByb3RvdHlwZS5fc3RyaW5nUmFuZ2VzVG9DZWxsUmFuZ2VzPWZ1bmN0aW9uKGUsdCxyKXt2YXIgaT0wLG49ITEsbz0wLHM9ZVtpXTtpZihzKXtmb3IodmFyIGE9cjthPHRoaXMuX2J1ZmZlclNlcnZpY2UuY29sczthKyspe3ZhciBsPXQuZ2V0V2lkdGgoYSksdT10LmdldFN0cmluZyhhKS5sZW5ndGh8fGMuV0hJVEVTUEFDRV9DRUxMX0NIQVIubGVuZ3RoO2lmKDAhPT1sKXtpZighbiYmc1swXTw9byYmKHNbMF09YSxuPSEwKSxzWzFdPD1vKXtpZihzWzFdPWEsIShzPWVbKytpXSkpYnJlYWs7c1swXTw9bz8oc1swXT1hLG49ITApOm49ITF9bys9dX19cyYmKHNbMV09dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKX19LGUuX21lcmdlUmFuZ2VzPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPSExLGk9MDtpPGUubGVuZ3RoO2krKyl7dmFyIG49ZVtpXTtpZihyKXtpZih0WzFdPD1uWzBdKXJldHVybiBlW2ktMV1bMV09dFsxXSxlO2lmKHRbMV08PW5bMV0pcmV0dXJuIGVbaS0xXVsxXT1NYXRoLm1heCh0WzFdLG5bMV0pLGUuc3BsaWNlKGksMSksZTtlLnNwbGljZShpLDEpLGktLX1lbHNle2lmKHRbMV08PW5bMF0pcmV0dXJuIGUuc3BsaWNlKGksMCx0KSxlO2lmKHRbMV08PW5bMV0pcmV0dXJuIG5bMF09TWF0aC5taW4odFswXSxuWzBdKSxlO3RbMF08blsxXSYmKG5bMF09TWF0aC5taW4odFswXSxuWzBdKSxyPSEwKX19cmV0dXJuIHI/ZVtlLmxlbmd0aC0xXVsxXT10WzFdOmUucHVzaCh0KSxlfSxlPW8oW3MoMCx1LklCdWZmZXJTZXJ2aWNlKV0sZSl9KCk7dC5DaGFyYWN0ZXJKb2luZXJTZXJ2aWNlPWZ9LDUxMTQ6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Db3JlQnJvd3NlclNlcnZpY2U9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl90ZXh0YXJlYT1lfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImlzRm9jdXNlZCIse2dldDpmdW5jdGlvbigpe3JldHVybih0aGlzLl90ZXh0YXJlYS5nZXRSb290Tm9kZT90aGlzLl90ZXh0YXJlYS5nZXRSb290Tm9kZSgpOmRvY3VtZW50KS5hY3RpdmVFbGVtZW50PT09dGhpcy5fdGV4dGFyZWEmJmRvY3VtZW50Lmhhc0ZvY3VzKCl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZX0oKTt0LkNvcmVCcm93c2VyU2VydmljZT1yfSw4OTM0OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaT10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LG49dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuTW91c2VTZXJ2aWNlPXZvaWQgMDt2YXIgbz1yKDQ3MjUpLHM9cig5ODA2KSxhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQpe3RoaXMuX3JlbmRlclNlcnZpY2U9ZSx0aGlzLl9jaGFyU2l6ZVNlcnZpY2U9dH1yZXR1cm4gZS5wcm90b3R5cGUuZ2V0Q29vcmRzPWZ1bmN0aW9uKGUsdCxyLGksbil7cmV0dXJuKDAscy5nZXRDb29yZHMpKGUsdCxyLGksdGhpcy5fY2hhclNpemVTZXJ2aWNlLmhhc1ZhbGlkU2l6ZSx0aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuYWN0dWFsQ2VsbFdpZHRoLHRoaXMuX3JlbmRlclNlcnZpY2UuZGltZW5zaW9ucy5hY3R1YWxDZWxsSGVpZ2h0LG4pfSxlLnByb3RvdHlwZS5nZXRSYXdCeXRlQ29vcmRzPWZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuPXRoaXMuZ2V0Q29vcmRzKGUsdCxyLGkpO3JldHVybigwLHMuZ2V0UmF3Qnl0ZUNvb3Jkcykobil9LGkoW24oMCxvLklSZW5kZXJTZXJ2aWNlKSxuKDEsby5JQ2hhclNpemVTZXJ2aWNlKV0sZSl9KCk7dC5Nb3VzZVNlcnZpY2U9YX0sMzIzMDpmdW5jdGlvbihlLHQscil7dmFyIGksbj10aGlzJiZ0aGlzLl9fZXh0ZW5kc3x8KGk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gaT1PYmplY3Quc2V0UHJvdG90eXBlT2Z8fHtfX3Byb3RvX186W119aW5zdGFuY2VvZiBBcnJheSYmZnVuY3Rpb24oZSx0KXtlLl9fcHJvdG9fXz10fXx8ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHIgaW4gdClPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodCxyKSYmKGVbcl09dFtyXSl9LGkoZSx0KX0sZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgdCYmbnVsbCE9PXQpdGhyb3cgbmV3IFR5cGVFcnJvcigiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSAiK1N0cmluZyh0KSsiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGwiKTtmdW5jdGlvbiByKCl7dGhpcy5jb25zdHJ1Y3Rvcj1lfWkoZSx0KSxlLnByb3RvdHlwZT1udWxsPT09dD9PYmplY3QuY3JlYXRlKHQpOihyLnByb3RvdHlwZT10LnByb3RvdHlwZSxuZXcgcil9KSxvPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30scz10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5SZW5kZXJTZXJ2aWNlPXZvaWQgMDt2YXIgYT1yKDYxOTMpLGM9cig4NDYwKSxsPXIoODQ0KSx1PXIoNTU5NiksaD1yKDM2NTYpLGY9cigyNTg1KSxfPXIoNDcyNSksZD1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpLG4sbyxzKXt2YXIgbD1lLmNhbGwodGhpcyl8fHRoaXM7aWYobC5fcmVuZGVyZXI9dCxsLl9yb3dDb3VudD1yLGwuX2NoYXJTaXplU2VydmljZT1vLGwuX2lzUGF1c2VkPSExLGwuX25lZWRzRnVsbFJlZnJlc2g9ITEsbC5faXNOZXh0UmVuZGVyUmVkcmF3T25seT0hMCxsLl9uZWVkc1NlbGVjdGlvblJlZnJlc2g9ITEsbC5fY2FudmFzV2lkdGg9MCxsLl9jYW52YXNIZWlnaHQ9MCxsLl9zZWxlY3Rpb25TdGF0ZT17c3RhcnQ6dm9pZCAwLGVuZDp2b2lkIDAsY29sdW1uU2VsZWN0TW9kZTohMX0sbC5fb25EaW1lbnNpb25zQ2hhbmdlPW5ldyBjLkV2ZW50RW1pdHRlcixsLl9vblJlbmRlcj1uZXcgYy5FdmVudEVtaXR0ZXIsbC5fb25SZWZyZXNoUmVxdWVzdD1uZXcgYy5FdmVudEVtaXR0ZXIsbC5yZWdpc3Rlcih7ZGlzcG9zZTpmdW5jdGlvbigpe3JldHVybiBsLl9yZW5kZXJlci5kaXNwb3NlKCl9fSksbC5fcmVuZGVyRGVib3VuY2VyPW5ldyBhLlJlbmRlckRlYm91bmNlcigoZnVuY3Rpb24oZSx0KXtyZXR1cm4gbC5fcmVuZGVyUm93cyhlLHQpfSkpLGwucmVnaXN0ZXIobC5fcmVuZGVyRGVib3VuY2VyKSxsLl9zY3JlZW5EcHJNb25pdG9yPW5ldyB1LlNjcmVlbkRwck1vbml0b3IsbC5fc2NyZWVuRHByTW9uaXRvci5zZXRMaXN0ZW5lcigoZnVuY3Rpb24oKXtyZXR1cm4gbC5vbkRldmljZVBpeGVsUmF0aW9DaGFuZ2UoKX0pKSxsLnJlZ2lzdGVyKGwuX3NjcmVlbkRwck1vbml0b3IpLGwucmVnaXN0ZXIocy5vblJlc2l6ZSgoZnVuY3Rpb24oZSl7cmV0dXJuIGwuX2Z1bGxSZWZyZXNoKCl9KSkpLGwucmVnaXN0ZXIobi5vbk9wdGlvbkNoYW5nZSgoZnVuY3Rpb24oKXtyZXR1cm4gbC5fcmVuZGVyZXIub25PcHRpb25zQ2hhbmdlZCgpfSkpKSxsLnJlZ2lzdGVyKGwuX2NoYXJTaXplU2VydmljZS5vbkNoYXJTaXplQ2hhbmdlKChmdW5jdGlvbigpe3JldHVybiBsLm9uQ2hhclNpemVDaGFuZ2VkKCl9KSkpLGwuX3JlbmRlcmVyLm9uUmVxdWVzdFJlZHJhdygoZnVuY3Rpb24oZSl7cmV0dXJuIGwucmVmcmVzaFJvd3MoZS5zdGFydCxlLmVuZCwhMCl9KSksbC5yZWdpc3RlcigoMCxoLmFkZERpc3Bvc2FibGVEb21MaXN0ZW5lcikod2luZG93LCJyZXNpemUiLChmdW5jdGlvbigpe3JldHVybiBsLm9uRGV2aWNlUGl4ZWxSYXRpb0NoYW5nZSgpfSkpKSwiSW50ZXJzZWN0aW9uT2JzZXJ2ZXIiaW4gd2luZG93KXt2YXIgZj1uZXcgSW50ZXJzZWN0aW9uT2JzZXJ2ZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBsLl9vbkludGVyc2VjdGlvbkNoYW5nZShlW2UubGVuZ3RoLTFdKX0pLHt0aHJlc2hvbGQ6MH0pO2Yub2JzZXJ2ZShpKSxsLnJlZ2lzdGVyKHtkaXNwb3NlOmZ1bmN0aW9uKCl7cmV0dXJuIGYuZGlzY29ubmVjdCgpfX0pfXJldHVybiBsfXJldHVybiBuKHQsZSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkRpbWVuc2lvbnNDaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25EaW1lbnNpb25zQ2hhbmdlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZW5kZXJlZEJ1ZmZlckNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlbmRlci5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uUmVmcmVzaFJlcXVlc3QiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZWZyZXNoUmVxdWVzdC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsImRpbWVuc2lvbnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fcmVuZGVyZXIuZGltZW5zaW9uc30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSx0LnByb3RvdHlwZS5fb25JbnRlcnNlY3Rpb25DaGFuZ2U9ZnVuY3Rpb24oZSl7dGhpcy5faXNQYXVzZWQ9dm9pZCAwPT09ZS5pc0ludGVyc2VjdGluZz8wPT09ZS5pbnRlcnNlY3Rpb25SYXRpbzohZS5pc0ludGVyc2VjdGluZyx0aGlzLl9pc1BhdXNlZHx8dGhpcy5fY2hhclNpemVTZXJ2aWNlLmhhc1ZhbGlkU2l6ZXx8dGhpcy5fY2hhclNpemVTZXJ2aWNlLm1lYXN1cmUoKSwhdGhpcy5faXNQYXVzZWQmJnRoaXMuX25lZWRzRnVsbFJlZnJlc2gmJih0aGlzLnJlZnJlc2hSb3dzKDAsdGhpcy5fcm93Q291bnQtMSksdGhpcy5fbmVlZHNGdWxsUmVmcmVzaD0hMSl9LHQucHJvdG90eXBlLnJlZnJlc2hSb3dzPWZ1bmN0aW9uKGUsdCxyKXt2b2lkIDA9PT1yJiYocj0hMSksdGhpcy5faXNQYXVzZWQ/dGhpcy5fbmVlZHNGdWxsUmVmcmVzaD0hMDoocnx8KHRoaXMuX2lzTmV4dFJlbmRlclJlZHJhd09ubHk9ITEpLHRoaXMuX3JlbmRlckRlYm91bmNlci5yZWZyZXNoKGUsdCx0aGlzLl9yb3dDb3VudCkpfSx0LnByb3RvdHlwZS5fcmVuZGVyUm93cz1mdW5jdGlvbihlLHQpe3RoaXMuX3JlbmRlcmVyLnJlbmRlclJvd3MoZSx0KSx0aGlzLl9uZWVkc1NlbGVjdGlvblJlZnJlc2gmJih0aGlzLl9yZW5kZXJlci5vblNlbGVjdGlvbkNoYW5nZWQodGhpcy5fc2VsZWN0aW9uU3RhdGUuc3RhcnQsdGhpcy5fc2VsZWN0aW9uU3RhdGUuZW5kLHRoaXMuX3NlbGVjdGlvblN0YXRlLmNvbHVtblNlbGVjdE1vZGUpLHRoaXMuX25lZWRzU2VsZWN0aW9uUmVmcmVzaD0hMSksdGhpcy5faXNOZXh0UmVuZGVyUmVkcmF3T25seXx8dGhpcy5fb25SZW5kZXIuZmlyZSh7c3RhcnQ6ZSxlbmQ6dH0pLHRoaXMuX2lzTmV4dFJlbmRlclJlZHJhd09ubHk9ITB9LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuX3Jvd0NvdW50PXQsdGhpcy5fZmlyZU9uQ2FudmFzUmVzaXplKCl9LHQucHJvdG90eXBlLmNoYW5nZU9wdGlvbnM9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5vbk9wdGlvbnNDaGFuZ2VkKCksdGhpcy5yZWZyZXNoUm93cygwLHRoaXMuX3Jvd0NvdW50LTEpLHRoaXMuX2ZpcmVPbkNhbnZhc1Jlc2l6ZSgpfSx0LnByb3RvdHlwZS5fZmlyZU9uQ2FudmFzUmVzaXplPWZ1bmN0aW9uKCl7dGhpcy5fcmVuZGVyZXIuZGltZW5zaW9ucy5jYW52YXNXaWR0aD09PXRoaXMuX2NhbnZhc1dpZHRoJiZ0aGlzLl9yZW5kZXJlci5kaW1lbnNpb25zLmNhbnZhc0hlaWdodD09PXRoaXMuX2NhbnZhc0hlaWdodHx8dGhpcy5fb25EaW1lbnNpb25zQ2hhbmdlLmZpcmUodGhpcy5fcmVuZGVyZXIuZGltZW5zaW9ucyl9LHQucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXtlLnByb3RvdHlwZS5kaXNwb3NlLmNhbGwodGhpcyl9LHQucHJvdG90eXBlLnNldFJlbmRlcmVyPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7dGhpcy5fcmVuZGVyZXIuZGlzcG9zZSgpLHRoaXMuX3JlbmRlcmVyPWUsdGhpcy5fcmVuZGVyZXIub25SZXF1ZXN0UmVkcmF3KChmdW5jdGlvbihlKXtyZXR1cm4gdC5yZWZyZXNoUm93cyhlLnN0YXJ0LGUuZW5kLCEwKX0pKSx0aGlzLl9uZWVkc1NlbGVjdGlvblJlZnJlc2g9ITAsdGhpcy5fZnVsbFJlZnJlc2goKX0sdC5wcm90b3R5cGUuX2Z1bGxSZWZyZXNoPWZ1bmN0aW9uKCl7dGhpcy5faXNQYXVzZWQ/dGhpcy5fbmVlZHNGdWxsUmVmcmVzaD0hMDp0aGlzLnJlZnJlc2hSb3dzKDAsdGhpcy5fcm93Q291bnQtMSl9LHQucHJvdG90eXBlLmNsZWFyVGV4dHVyZUF0bGFzPWZ1bmN0aW9uKCl7dmFyIGUsdDtudWxsPT09KHQ9bnVsbD09PShlPXRoaXMuX3JlbmRlcmVyKXx8dm9pZCAwPT09ZT92b2lkIDA6ZS5jbGVhclRleHR1cmVBdGxhcyl8fHZvaWQgMD09PXR8fHQuY2FsbChlKSx0aGlzLl9mdWxsUmVmcmVzaCgpfSx0LnByb3RvdHlwZS5zZXRDb2xvcnM9ZnVuY3Rpb24oZSl7dGhpcy5fcmVuZGVyZXIuc2V0Q29sb3JzKGUpLHRoaXMuX2Z1bGxSZWZyZXNoKCl9LHQucHJvdG90eXBlLm9uRGV2aWNlUGl4ZWxSYXRpb0NoYW5nZT1mdW5jdGlvbigpe3RoaXMuX2NoYXJTaXplU2VydmljZS5tZWFzdXJlKCksdGhpcy5fcmVuZGVyZXIub25EZXZpY2VQaXhlbFJhdGlvQ2hhbmdlKCksdGhpcy5yZWZyZXNoUm93cygwLHRoaXMuX3Jvd0NvdW50LTEpfSx0LnByb3RvdHlwZS5vblJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuX3JlbmRlcmVyLm9uUmVzaXplKGUsdCksdGhpcy5fZnVsbFJlZnJlc2goKX0sdC5wcm90b3R5cGUub25DaGFyU2l6ZUNoYW5nZWQ9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5vbkNoYXJTaXplQ2hhbmdlZCgpfSx0LnByb3RvdHlwZS5vbkJsdXI9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5vbkJsdXIoKX0sdC5wcm90b3R5cGUub25Gb2N1cz1mdW5jdGlvbigpe3RoaXMuX3JlbmRlcmVyLm9uRm9jdXMoKX0sdC5wcm90b3R5cGUub25TZWxlY3Rpb25DaGFuZ2VkPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9zZWxlY3Rpb25TdGF0ZS5zdGFydD1lLHRoaXMuX3NlbGVjdGlvblN0YXRlLmVuZD10LHRoaXMuX3NlbGVjdGlvblN0YXRlLmNvbHVtblNlbGVjdE1vZGU9cix0aGlzLl9yZW5kZXJlci5vblNlbGVjdGlvbkNoYW5nZWQoZSx0LHIpfSx0LnByb3RvdHlwZS5vbkN1cnNvck1vdmU9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5vbkN1cnNvck1vdmUoKX0sdC5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXt0aGlzLl9yZW5kZXJlci5jbGVhcigpfSxvKFtzKDMsZi5JT3B0aW9uc1NlcnZpY2UpLHMoNCxfLklDaGFyU2l6ZVNlcnZpY2UpLHMoNSxmLklCdWZmZXJTZXJ2aWNlKV0sdCl9KGwuRGlzcG9zYWJsZSk7dC5SZW5kZXJTZXJ2aWNlPWR9LDkzMTI6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSksbz10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LHM9dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuU2VsZWN0aW9uU2VydmljZT12b2lkIDA7dmFyIGE9cig2MTE0KSxjPXIoNDU2KSxsPXIoNTExKSx1PXIoODQ2MCksaD1yKDQ3MjUpLGY9cigyNTg1KSxfPXIoOTgwNiksZD1yKDk1MDQpLHA9cig4NDQpLHY9cig0ODQxKSxnPVN0cmluZy5mcm9tQ2hhckNvZGUoMTYwKSx5PW5ldyBSZWdFeHAoZywiZyIpLG09ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8scyxhLGgpe3ZhciBmPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gZi5fZWxlbWVudD10LGYuX3NjcmVlbkVsZW1lbnQ9cixmLl9saW5raWZpZXI9aSxmLl9idWZmZXJTZXJ2aWNlPW4sZi5fY29yZVNlcnZpY2U9byxmLl9tb3VzZVNlcnZpY2U9cyxmLl9vcHRpb25zU2VydmljZT1hLGYuX3JlbmRlclNlcnZpY2U9aCxmLl9kcmFnU2Nyb2xsQW1vdW50PTAsZi5fZW5hYmxlZD0hMCxmLl93b3JrQ2VsbD1uZXcgbC5DZWxsRGF0YSxmLl9tb3VzZURvd25UaW1lU3RhbXA9MCxmLl9vbGRIYXNTZWxlY3Rpb249ITEsZi5fb2xkU2VsZWN0aW9uU3RhcnQ9dm9pZCAwLGYuX29sZFNlbGVjdGlvbkVuZD12b2lkIDAsZi5fb25MaW51eE1vdXNlU2VsZWN0aW9uPWYucmVnaXN0ZXIobmV3IHUuRXZlbnRFbWl0dGVyKSxmLl9vblJlZHJhd1JlcXVlc3Q9Zi5yZWdpc3RlcihuZXcgdS5FdmVudEVtaXR0ZXIpLGYuX29uU2VsZWN0aW9uQ2hhbmdlPWYucmVnaXN0ZXIobmV3IHUuRXZlbnRFbWl0dGVyKSxmLl9vblJlcXVlc3RTY3JvbGxMaW5lcz1mLnJlZ2lzdGVyKG5ldyB1LkV2ZW50RW1pdHRlciksZi5fbW91c2VNb3ZlTGlzdGVuZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIGYuX29uTW91c2VNb3ZlKGUpfSxmLl9tb3VzZVVwTGlzdGVuZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIGYuX29uTW91c2VVcChlKX0sZi5fY29yZVNlcnZpY2Uub25Vc2VySW5wdXQoKGZ1bmN0aW9uKCl7Zi5oYXNTZWxlY3Rpb24mJmYuY2xlYXJTZWxlY3Rpb24oKX0pKSxmLl90cmltTGlzdGVuZXI9Zi5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMub25UcmltKChmdW5jdGlvbihlKXtyZXR1cm4gZi5fb25UcmltKGUpfSkpLGYucmVnaXN0ZXIoZi5fYnVmZmVyU2VydmljZS5idWZmZXJzLm9uQnVmZmVyQWN0aXZhdGUoKGZ1bmN0aW9uKGUpe3JldHVybiBmLl9vbkJ1ZmZlckFjdGl2YXRlKGUpfSkpKSxmLmVuYWJsZSgpLGYuX21vZGVsPW5ldyBjLlNlbGVjdGlvbk1vZGVsKGYuX2J1ZmZlclNlcnZpY2UpLGYuX2FjdGl2ZVNlbGVjdGlvbk1vZGU9MCxmfXJldHVybiBuKHQsZSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkxpbnV4TW91c2VTZWxlY3Rpb24iLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25MaW51eE1vdXNlU2VsZWN0aW9uLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0UmVkcmF3Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uUmVkcmF3UmVxdWVzdC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uU2VsZWN0aW9uQ2hhbmdlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uU2VsZWN0aW9uQ2hhbmdlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0U2Nyb2xsTGluZXMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZXF1ZXN0U2Nyb2xsTGluZXMuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3RoaXMuX3JlbW92ZU1vdXNlRG93bkxpc3RlbmVycygpfSx0LnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe3RoaXMuY2xlYXJTZWxlY3Rpb24oKX0sdC5wcm90b3R5cGUuZGlzYWJsZT1mdW5jdGlvbigpe3RoaXMuY2xlYXJTZWxlY3Rpb24oKSx0aGlzLl9lbmFibGVkPSExfSx0LnByb3RvdHlwZS5lbmFibGU9ZnVuY3Rpb24oKXt0aGlzLl9lbmFibGVkPSEwfSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsInNlbGVjdGlvblN0YXJ0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uU3RhcnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJzZWxlY3Rpb25FbmQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fbW9kZWwuZmluYWxTZWxlY3Rpb25FbmR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJoYXNTZWxlY3Rpb24iLHtnZXQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLl9tb2RlbC5maW5hbFNlbGVjdGlvblN0YXJ0LHQ9dGhpcy5fbW9kZWwuZmluYWxTZWxlY3Rpb25FbmQ7cmV0dXJuISghZXx8IXR8fGVbMF09PT10WzBdJiZlWzFdPT09dFsxXSl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJzZWxlY3Rpb25UZXh0Iix7Z2V0OmZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5fbW9kZWwuZmluYWxTZWxlY3Rpb25TdGFydCx0PXRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uRW5kO2lmKCFlfHwhdClyZXR1cm4iIjt2YXIgcj10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlcixpPVtdO2lmKDM9PT10aGlzLl9hY3RpdmVTZWxlY3Rpb25Nb2RlKXtpZihlWzBdPT09dFswXSlyZXR1cm4iIjtmb3IodmFyIG49ZVsxXTtuPD10WzFdO24rKyl7dmFyIG89ci50cmFuc2xhdGVCdWZmZXJMaW5lVG9TdHJpbmcobiwhMCxlWzBdLHRbMF0pO2kucHVzaChvKX19ZWxzZXt2YXIgcz1lWzFdPT09dFsxXT90WzBdOnZvaWQgMDtmb3IoaS5wdXNoKHIudHJhbnNsYXRlQnVmZmVyTGluZVRvU3RyaW5nKGVbMV0sITAsZVswXSxzKSksbj1lWzFdKzE7bjw9dFsxXS0xO24rKyl7dmFyIGM9ci5saW5lcy5nZXQobik7bz1yLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhuLCEwKSwobnVsbD09Yz92b2lkIDA6Yy5pc1dyYXBwZWQpP2lbaS5sZW5ndGgtMV0rPW86aS5wdXNoKG8pfWVbMV0hPT10WzFdJiYoYz1yLmxpbmVzLmdldCh0WzFdKSxvPXIudHJhbnNsYXRlQnVmZmVyTGluZVRvU3RyaW5nKHRbMV0sITAsMCx0WzBdKSxjJiZjLmlzV3JhcHBlZD9pW2kubGVuZ3RoLTFdKz1vOmkucHVzaChvKSl9cmV0dXJuIGkubWFwKChmdW5jdGlvbihlKXtyZXR1cm4gZS5yZXBsYWNlKHksIiAiKX0pKS5qb2luKGEuaXNXaW5kb3dzPyJcclxuIjoiXG4iKX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSx0LnByb3RvdHlwZS5jbGVhclNlbGVjdGlvbj1mdW5jdGlvbigpe3RoaXMuX21vZGVsLmNsZWFyU2VsZWN0aW9uKCksdGhpcy5fcmVtb3ZlTW91c2VEb3duTGlzdGVuZXJzKCksdGhpcy5yZWZyZXNoKCksdGhpcy5fb25TZWxlY3Rpb25DaGFuZ2UuZmlyZSgpfSx0LnByb3RvdHlwZS5yZWZyZXNoPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXM7dGhpcy5fcmVmcmVzaEFuaW1hdGlvbkZyYW1lfHwodGhpcy5fcmVmcmVzaEFuaW1hdGlvbkZyYW1lPXdpbmRvdy5yZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKGZ1bmN0aW9uKCl7cmV0dXJuIHQuX3JlZnJlc2goKX0pKSksYS5pc0xpbnV4JiZlJiZ0aGlzLnNlbGVjdGlvblRleHQubGVuZ3RoJiZ0aGlzLl9vbkxpbnV4TW91c2VTZWxlY3Rpb24uZmlyZSh0aGlzLnNlbGVjdGlvblRleHQpfSx0LnByb3RvdHlwZS5fcmVmcmVzaD1mdW5jdGlvbigpe3RoaXMuX3JlZnJlc2hBbmltYXRpb25GcmFtZT12b2lkIDAsdGhpcy5fb25SZWRyYXdSZXF1ZXN0LmZpcmUoe3N0YXJ0OnRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uU3RhcnQsZW5kOnRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uRW5kLGNvbHVtblNlbGVjdE1vZGU6Mz09PXRoaXMuX2FjdGl2ZVNlbGVjdGlvbk1vZGV9KX0sdC5wcm90b3R5cGUuX2lzQ2xpY2tJblNlbGVjdGlvbj1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9nZXRNb3VzZUJ1ZmZlckNvb3JkcyhlKSxyPXRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uU3RhcnQsaT10aGlzLl9tb2RlbC5maW5hbFNlbGVjdGlvbkVuZDtyZXR1cm4hIShyJiZpJiZ0KSYmdGhpcy5fYXJlQ29vcmRzSW5TZWxlY3Rpb24odCxyLGkpfSx0LnByb3RvdHlwZS5fYXJlQ29vcmRzSW5TZWxlY3Rpb249ZnVuY3Rpb24oZSx0LHIpe3JldHVybiBlWzFdPnRbMV0mJmVbMV08clsxXXx8dFsxXT09PXJbMV0mJmVbMV09PT10WzFdJiZlWzBdPj10WzBdJiZlWzBdPHJbMF18fHRbMV08clsxXSYmZVsxXT09PXJbMV0mJmVbMF08clswXXx8dFsxXTxyWzFdJiZlWzFdPT09dFsxXSYmZVswXT49dFswXX0sdC5wcm90b3R5cGUuX3NlbGVjdFdvcmRBdEN1cnNvcj1mdW5jdGlvbihlLHQpe3ZhciByLGksbj1udWxsPT09KGk9bnVsbD09PShyPXRoaXMuX2xpbmtpZmllci5jdXJyZW50TGluayl8fHZvaWQgMD09PXI/dm9pZCAwOnIubGluayl8fHZvaWQgMD09PWk/dm9pZCAwOmkucmFuZ2U7aWYobilyZXR1cm4gdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQ9W24uc3RhcnQueC0xLG4uc3RhcnQueS0xXSx0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydExlbmd0aD0oMCx2LmdldFJhbmdlTGVuZ3RoKShuLHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyksdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kPXZvaWQgMCwhMDt2YXIgbz10aGlzLl9nZXRNb3VzZUJ1ZmZlckNvb3JkcyhlKTtyZXR1cm4hIW8mJih0aGlzLl9zZWxlY3RXb3JkQXQobyx0KSx0aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmQ9dm9pZCAwLCEwKX0sdC5wcm90b3R5cGUuc2VsZWN0QWxsPWZ1bmN0aW9uKCl7dGhpcy5fbW9kZWwuaXNTZWxlY3RBbGxBY3RpdmU9ITAsdGhpcy5yZWZyZXNoKCksdGhpcy5fb25TZWxlY3Rpb25DaGFuZ2UuZmlyZSgpfSx0LnByb3RvdHlwZS5zZWxlY3RMaW5lcz1mdW5jdGlvbihlLHQpe3RoaXMuX21vZGVsLmNsZWFyU2VsZWN0aW9uKCksZT1NYXRoLm1heChlLDApLHQ9TWF0aC5taW4odCx0aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci5saW5lcy5sZW5ndGgtMSksdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQ9WzAsZV0sdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kPVt0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdF0sdGhpcy5yZWZyZXNoKCksdGhpcy5fb25TZWxlY3Rpb25DaGFuZ2UuZmlyZSgpfSx0LnByb3RvdHlwZS5fb25UcmltPWZ1bmN0aW9uKGUpe3RoaXMuX21vZGVsLm9uVHJpbShlKSYmdGhpcy5yZWZyZXNoKCl9LHQucHJvdG90eXBlLl9nZXRNb3VzZUJ1ZmZlckNvb3Jkcz1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9tb3VzZVNlcnZpY2UuZ2V0Q29vcmRzKGUsdGhpcy5fc2NyZWVuRWxlbWVudCx0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLCEwKTtpZih0KXJldHVybiB0WzBdLS0sdFsxXS0tLHRbMV0rPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnlkaXNwLHR9LHQucHJvdG90eXBlLl9nZXRNb3VzZUV2ZW50U2Nyb2xsQW1vdW50PWZ1bmN0aW9uKGUpe3ZhciB0PSgwLF8uZ2V0Q29vcmRzUmVsYXRpdmVUb0VsZW1lbnQpKGUsdGhpcy5fc2NyZWVuRWxlbWVudClbMV0scj10aGlzLl9yZW5kZXJTZXJ2aWNlLmRpbWVuc2lvbnMuY2FudmFzSGVpZ2h0O3JldHVybiB0Pj0wJiZ0PD1yPzA6KHQ+ciYmKHQtPXIpLHQ9TWF0aC5taW4oTWF0aC5tYXgodCwtNTApLDUwKSwodC89NTApL01hdGguYWJzKHQpK01hdGgucm91bmQoMTQqdCkpfSx0LnByb3RvdHlwZS5zaG91bGRGb3JjZVNlbGVjdGlvbj1mdW5jdGlvbihlKXtyZXR1cm4gYS5pc01hYz9lLmFsdEtleSYmdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5tYWNPcHRpb25DbGlja0ZvcmNlc1NlbGVjdGlvbjplLnNoaWZ0S2V5fSx0LnByb3RvdHlwZS5vbk1vdXNlRG93bj1mdW5jdGlvbihlKXtpZih0aGlzLl9tb3VzZURvd25UaW1lU3RhbXA9ZS50aW1lU3RhbXAsKDIhPT1lLmJ1dHRvbnx8IXRoaXMuaGFzU2VsZWN0aW9uKSYmMD09PWUuYnV0dG9uKXtpZighdGhpcy5fZW5hYmxlZCl7aWYoIXRoaXMuc2hvdWxkRm9yY2VTZWxlY3Rpb24oZSkpcmV0dXJuO2Uuc3RvcFByb3BhZ2F0aW9uKCl9ZS5wcmV2ZW50RGVmYXVsdCgpLHRoaXMuX2RyYWdTY3JvbGxBbW91bnQ9MCx0aGlzLl9lbmFibGVkJiZlLnNoaWZ0S2V5P3RoaXMuX29uSW5jcmVtZW50YWxDbGljayhlKToxPT09ZS5kZXRhaWw/dGhpcy5fb25TaW5nbGVDbGljayhlKToyPT09ZS5kZXRhaWw/dGhpcy5fb25Eb3VibGVDbGljayhlKTozPT09ZS5kZXRhaWwmJnRoaXMuX29uVHJpcGxlQ2xpY2soZSksdGhpcy5fYWRkTW91c2VEb3duTGlzdGVuZXJzKCksdGhpcy5yZWZyZXNoKCEwKX19LHQucHJvdG90eXBlLl9hZGRNb3VzZURvd25MaXN0ZW5lcnM9ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3RoaXMuX3NjcmVlbkVsZW1lbnQub3duZXJEb2N1bWVudCYmKHRoaXMuX3NjcmVlbkVsZW1lbnQub3duZXJEb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCJtb3VzZW1vdmUiLHRoaXMuX21vdXNlTW92ZUxpc3RlbmVyKSx0aGlzLl9zY3JlZW5FbGVtZW50Lm93bmVyRG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcigibW91c2V1cCIsdGhpcy5fbW91c2VVcExpc3RlbmVyKSksdGhpcy5fZHJhZ1Njcm9sbEludGVydmFsVGltZXI9d2luZG93LnNldEludGVydmFsKChmdW5jdGlvbigpe3JldHVybiBlLl9kcmFnU2Nyb2xsKCl9KSw1MCl9LHQucHJvdG90eXBlLl9yZW1vdmVNb3VzZURvd25MaXN0ZW5lcnM9ZnVuY3Rpb24oKXt0aGlzLl9zY3JlZW5FbGVtZW50Lm93bmVyRG9jdW1lbnQmJih0aGlzLl9zY3JlZW5FbGVtZW50Lm93bmVyRG9jdW1lbnQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibW91c2Vtb3ZlIix0aGlzLl9tb3VzZU1vdmVMaXN0ZW5lciksdGhpcy5fc2NyZWVuRWxlbWVudC5vd25lckRvY3VtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoIm1vdXNldXAiLHRoaXMuX21vdXNlVXBMaXN0ZW5lcikpLGNsZWFySW50ZXJ2YWwodGhpcy5fZHJhZ1Njcm9sbEludGVydmFsVGltZXIpLHRoaXMuX2RyYWdTY3JvbGxJbnRlcnZhbFRpbWVyPXZvaWQgMH0sdC5wcm90b3R5cGUuX29uSW5jcmVtZW50YWxDbGljaz1mdW5jdGlvbihlKXt0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydCYmKHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZD10aGlzLl9nZXRNb3VzZUJ1ZmZlckNvb3JkcyhlKSl9LHQucHJvdG90eXBlLl9vblNpbmdsZUNsaWNrPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0TGVuZ3RoPTAsdGhpcy5fbW9kZWwuaXNTZWxlY3RBbGxBY3RpdmU9ITEsdGhpcy5fYWN0aXZlU2VsZWN0aW9uTW9kZT10aGlzLnNob3VsZENvbHVtblNlbGVjdChlKT8zOjAsdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQ9dGhpcy5fZ2V0TW91c2VCdWZmZXJDb29yZHMoZSksdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQpe3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZD12b2lkIDA7dmFyIHQ9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIubGluZXMuZ2V0KHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0WzFdKTt0JiZ0Lmxlbmd0aCE9PXRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0WzBdJiYwPT09dC5oYXNXaWR0aCh0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydFswXSkmJnRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0WzBdKyt9fSx0LnByb3RvdHlwZS5fb25Eb3VibGVDbGljaz1mdW5jdGlvbihlKXt0aGlzLl9zZWxlY3RXb3JkQXRDdXJzb3IoZSwhMCkmJih0aGlzLl9hY3RpdmVTZWxlY3Rpb25Nb2RlPTEpfSx0LnByb3RvdHlwZS5fb25UcmlwbGVDbGljaz1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9nZXRNb3VzZUJ1ZmZlckNvb3JkcyhlKTt0JiYodGhpcy5fYWN0aXZlU2VsZWN0aW9uTW9kZT0yLHRoaXMuX3NlbGVjdExpbmVBdCh0WzFdKSl9LHQucHJvdG90eXBlLnNob3VsZENvbHVtblNlbGVjdD1mdW5jdGlvbihlKXtyZXR1cm4gZS5hbHRLZXkmJiEoYS5pc01hYyYmdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5tYWNPcHRpb25DbGlja0ZvcmNlc1NlbGVjdGlvbil9LHQucHJvdG90eXBlLl9vbk1vdXNlTW92ZT1mdW5jdGlvbihlKXtpZihlLnN0b3BJbW1lZGlhdGVQcm9wYWdhdGlvbigpLHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0KXt2YXIgdD10aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmQ/W3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXSx0aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmRbMV1dOm51bGw7aWYodGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kPXRoaXMuX2dldE1vdXNlQnVmZmVyQ29vcmRzKGUpLHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZCl7Mj09PXRoaXMuX2FjdGl2ZVNlbGVjdGlvbk1vZGU/dGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzFdPHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0WzFdP3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXT0wOnRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM6MT09PXRoaXMuX2FjdGl2ZVNlbGVjdGlvbk1vZGUmJnRoaXMuX3NlbGVjdFRvV29yZEF0KHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZCksdGhpcy5fZHJhZ1Njcm9sbEFtb3VudD10aGlzLl9nZXRNb3VzZUV2ZW50U2Nyb2xsQW1vdW50KGUpLDMhPT10aGlzLl9hY3RpdmVTZWxlY3Rpb25Nb2RlJiYodGhpcy5fZHJhZ1Njcm9sbEFtb3VudD4wP3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM6dGhpcy5fZHJhZ1Njcm9sbEFtb3VudDwwJiYodGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzBdPTApKTt2YXIgcj10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlcjtpZih0aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmRbMV08ci5saW5lcy5sZW5ndGgpe3ZhciBpPXIubGluZXMuZ2V0KHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFsxXSk7aSYmMD09PWkuaGFzV2lkdGgodGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzBdKSYmdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzBdKyt9dCYmdFswXT09PXRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFswXSYmdFsxXT09PXRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZFsxXXx8dGhpcy5yZWZyZXNoKCEwKX1lbHNlIHRoaXMucmVmcmVzaCghMCl9fSx0LnByb3RvdHlwZS5fZHJhZ1Njcm9sbD1mdW5jdGlvbigpe2lmKHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZCYmdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnQmJnRoaXMuX2RyYWdTY3JvbGxBbW91bnQpe3RoaXMuX29uUmVxdWVzdFNjcm9sbExpbmVzLmZpcmUoe2Ftb3VudDp0aGlzLl9kcmFnU2Nyb2xsQW1vdW50LHN1cHByZXNzU2Nyb2xsRXZlbnQ6ITF9KTt2YXIgZT10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlcjt0aGlzLl9kcmFnU2Nyb2xsQW1vdW50PjA/KDMhPT10aGlzLl9hY3RpdmVTZWxlY3Rpb25Nb2RlJiYodGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzBdPXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyksdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzFdPU1hdGgubWluKGUueWRpc3ArdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLGUubGluZXMubGVuZ3RoLTEpKTooMyE9PXRoaXMuX2FjdGl2ZVNlbGVjdGlvbk1vZGUmJih0aGlzLl9tb2RlbC5zZWxlY3Rpb25FbmRbMF09MCksdGhpcy5fbW9kZWwuc2VsZWN0aW9uRW5kWzFdPWUueWRpc3ApLHRoaXMucmVmcmVzaCgpfX0sdC5wcm90b3R5cGUuX29uTW91c2VVcD1mdW5jdGlvbihlKXt2YXIgdD1lLnRpbWVTdGFtcC10aGlzLl9tb3VzZURvd25UaW1lU3RhbXA7aWYodGhpcy5fcmVtb3ZlTW91c2VEb3duTGlzdGVuZXJzKCksdGhpcy5zZWxlY3Rpb25UZXh0Lmxlbmd0aDw9MSYmdDw1MDAmJmUuYWx0S2V5JiZ0aGlzLl9vcHRpb25zU2VydmljZS5nZXRPcHRpb24oImFsdENsaWNrTW92ZXNDdXJzb3IiKSl7aWYodGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueWJhc2U9PT10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCl7dmFyIHI9dGhpcy5fbW91c2VTZXJ2aWNlLmdldENvb3JkcyhlLHRoaXMuX2VsZW1lbnQsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cywhMSk7aWYociYmdm9pZCAwIT09clswXSYmdm9pZCAwIT09clsxXSl7dmFyIGk9KDAsZC5tb3ZlVG9DZWxsU2VxdWVuY2UpKHJbMF0tMSxyWzFdLTEsdGhpcy5fYnVmZmVyU2VydmljZSx0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuYXBwbGljYXRpb25DdXJzb3JLZXlzKTt0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KGksITApfX19ZWxzZSB0aGlzLl9maXJlRXZlbnRJZlNlbGVjdGlvbkNoYW5nZWQoKX0sdC5wcm90b3R5cGUuX2ZpcmVFdmVudElmU2VsZWN0aW9uQ2hhbmdlZD1mdW5jdGlvbigpe3ZhciBlPXRoaXMuX21vZGVsLmZpbmFsU2VsZWN0aW9uU3RhcnQsdD10aGlzLl9tb2RlbC5maW5hbFNlbGVjdGlvbkVuZCxyPSEoIWV8fCF0fHxlWzBdPT09dFswXSYmZVsxXT09PXRbMV0pO3I/ZSYmdCYmKHRoaXMuX29sZFNlbGVjdGlvblN0YXJ0JiZ0aGlzLl9vbGRTZWxlY3Rpb25FbmQmJmVbMF09PT10aGlzLl9vbGRTZWxlY3Rpb25TdGFydFswXSYmZVsxXT09PXRoaXMuX29sZFNlbGVjdGlvblN0YXJ0WzFdJiZ0WzBdPT09dGhpcy5fb2xkU2VsZWN0aW9uRW5kWzBdJiZ0WzFdPT09dGhpcy5fb2xkU2VsZWN0aW9uRW5kWzFdfHx0aGlzLl9maXJlT25TZWxlY3Rpb25DaGFuZ2UoZSx0LHIpKTp0aGlzLl9vbGRIYXNTZWxlY3Rpb24mJnRoaXMuX2ZpcmVPblNlbGVjdGlvbkNoYW5nZShlLHQscil9LHQucHJvdG90eXBlLl9maXJlT25TZWxlY3Rpb25DaGFuZ2U9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX29sZFNlbGVjdGlvblN0YXJ0PWUsdGhpcy5fb2xkU2VsZWN0aW9uRW5kPXQsdGhpcy5fb2xkSGFzU2VsZWN0aW9uPXIsdGhpcy5fb25TZWxlY3Rpb25DaGFuZ2UuZmlyZSgpfSx0LnByb3RvdHlwZS5fb25CdWZmZXJBY3RpdmF0ZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzO3RoaXMuY2xlYXJTZWxlY3Rpb24oKSx0aGlzLl90cmltTGlzdGVuZXIuZGlzcG9zZSgpLHRoaXMuX3RyaW1MaXN0ZW5lcj1lLmFjdGl2ZUJ1ZmZlci5saW5lcy5vblRyaW0oKGZ1bmN0aW9uKGUpe3JldHVybiB0Ll9vblRyaW0oZSl9KSl9LHQucHJvdG90eXBlLl9jb252ZXJ0Vmlld3BvcnRDb2xUb0NoYXJhY3RlckluZGV4PWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRbMF0saT0wO3RbMF0+PWk7aSsrKXt2YXIgbj1lLmxvYWRDZWxsKGksdGhpcy5fd29ya0NlbGwpLmdldENoYXJzKCkubGVuZ3RoOzA9PT10aGlzLl93b3JrQ2VsbC5nZXRXaWR0aCgpP3ItLTpuPjEmJnRbMF0hPT1pJiYocis9bi0xKX1yZXR1cm4gcn0sdC5wcm90b3R5cGUuc2V0U2VsZWN0aW9uPWZ1bmN0aW9uKGUsdCxyKXt0aGlzLl9tb2RlbC5jbGVhclNlbGVjdGlvbigpLHRoaXMuX3JlbW92ZU1vdXNlRG93bkxpc3RlbmVycygpLHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0PVtlLHRdLHRoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0TGVuZ3RoPXIsdGhpcy5yZWZyZXNoKCl9LHQucHJvdG90eXBlLnJpZ2h0Q2xpY2tTZWxlY3Q9ZnVuY3Rpb24oZSl7dGhpcy5faXNDbGlja0luU2VsZWN0aW9uKGUpfHwodGhpcy5fc2VsZWN0V29yZEF0Q3Vyc29yKGUsITEpJiZ0aGlzLnJlZnJlc2goITApLHRoaXMuX2ZpcmVFdmVudElmU2VsZWN0aW9uQ2hhbmdlZCgpKX0sdC5wcm90b3R5cGUuX2dldFdvcmRBdD1mdW5jdGlvbihlLHQscixpKXtpZih2b2lkIDA9PT1yJiYocj0hMCksdm9pZCAwPT09aSYmKGk9ITApLCEoZVswXT49dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSl7dmFyIG49dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIsbz1uLmxpbmVzLmdldChlWzFdKTtpZihvKXt2YXIgcz1uLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhlWzFdLCExKSxhPXRoaXMuX2NvbnZlcnRWaWV3cG9ydENvbFRvQ2hhcmFjdGVySW5kZXgobyxlKSxjPWEsbD1lWzBdLWEsdT0wLGg9MCxmPTAsXz0wO2lmKCIgIj09PXMuY2hhckF0KGEpKXtmb3IoO2E+MCYmIiAiPT09cy5jaGFyQXQoYS0xKTspYS0tO2Zvcig7YzxzLmxlbmd0aCYmIiAiPT09cy5jaGFyQXQoYysxKTspYysrfWVsc2V7dmFyIGQ9ZVswXSxwPWVbMF07MD09PW8uZ2V0V2lkdGgoZCkmJih1KyssZC0tKSwyPT09by5nZXRXaWR0aChwKSYmKGgrKyxwKyspO3ZhciB2PW8uZ2V0U3RyaW5nKHApLmxlbmd0aDtmb3Iodj4xJiYoXys9di0xLGMrPXYtMSk7ZD4wJiZhPjAmJiF0aGlzLl9pc0NoYXJXb3JkU2VwYXJhdG9yKG8ubG9hZENlbGwoZC0xLHRoaXMuX3dvcmtDZWxsKSk7KXtvLmxvYWRDZWxsKGQtMSx0aGlzLl93b3JrQ2VsbCk7dmFyIGc9dGhpcy5fd29ya0NlbGwuZ2V0Q2hhcnMoKS5sZW5ndGg7MD09PXRoaXMuX3dvcmtDZWxsLmdldFdpZHRoKCk/KHUrKyxkLS0pOmc+MSYmKGYrPWctMSxhLT1nLTEpLGEtLSxkLS19Zm9yKDtwPG8ubGVuZ3RoJiZjKzE8cy5sZW5ndGgmJiF0aGlzLl9pc0NoYXJXb3JkU2VwYXJhdG9yKG8ubG9hZENlbGwocCsxLHRoaXMuX3dvcmtDZWxsKSk7KXtvLmxvYWRDZWxsKHArMSx0aGlzLl93b3JrQ2VsbCk7dmFyIHk9dGhpcy5fd29ya0NlbGwuZ2V0Q2hhcnMoKS5sZW5ndGg7Mj09PXRoaXMuX3dvcmtDZWxsLmdldFdpZHRoKCk/KGgrKyxwKyspOnk+MSYmKF8rPXktMSxjKz15LTEpLGMrKyxwKyt9fWMrKzt2YXIgbT1hK2wtdStmLGI9TWF0aC5taW4odGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGMtYSt1K2gtZi1fKTtpZih0fHwiIiE9PXMuc2xpY2UoYSxjKS50cmltKCkpe2lmKHImJjA9PT1tJiYzMiE9PW8uZ2V0Q29kZVBvaW50KDApKXt2YXIgUz1uLmxpbmVzLmdldChlWzFdLTEpO2lmKFMmJm8uaXNXcmFwcGVkJiYzMiE9PVMuZ2V0Q29kZVBvaW50KHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scy0xKSl7dmFyIEM9dGhpcy5fZ2V0V29yZEF0KFt0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtMSxlWzFdLTFdLCExLCEwLCExKTtpZihDKXt2YXIgdz10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtQy5zdGFydDttLT13LGIrPXd9fX1pZihpJiZtK2I9PT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMmJjMyIT09by5nZXRDb2RlUG9pbnQodGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLTEpKXt2YXIgTD1uLmxpbmVzLmdldChlWzFdKzEpO2lmKChudWxsPT1MP3ZvaWQgMDpMLmlzV3JhcHBlZCkmJjMyIT09TC5nZXRDb2RlUG9pbnQoMCkpe3ZhciBFPXRoaXMuX2dldFdvcmRBdChbMCxlWzFdKzFdLCExLCExLCEwKTtFJiYoYis9RS5sZW5ndGgpfX1yZXR1cm57c3RhcnQ6bSxsZW5ndGg6Yn19fX19LHQucHJvdG90eXBlLl9zZWxlY3RXb3JkQXQ9ZnVuY3Rpb24oZSx0KXt2YXIgcj10aGlzLl9nZXRXb3JkQXQoZSx0KTtpZihyKXtmb3IoO3Iuc3RhcnQ8MDspci5zdGFydCs9dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLGVbMV0tLTt0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydD1bci5zdGFydCxlWzFdXSx0aGlzLl9tb2RlbC5zZWxlY3Rpb25TdGFydExlbmd0aD1yLmxlbmd0aH19LHQucHJvdG90eXBlLl9zZWxlY3RUb1dvcmRBdD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9nZXRXb3JkQXQoZSwhMCk7aWYodCl7Zm9yKHZhciByPWVbMV07dC5zdGFydDwwOyl0LnN0YXJ0Kz10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsci0tO2lmKCF0aGlzLl9tb2RlbC5hcmVTZWxlY3Rpb25WYWx1ZXNSZXZlcnNlZCgpKWZvcig7dC5zdGFydCt0Lmxlbmd0aD50aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHM7KXQubGVuZ3RoLT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMscisrO3RoaXMuX21vZGVsLnNlbGVjdGlvbkVuZD1bdGhpcy5fbW9kZWwuYXJlU2VsZWN0aW9uVmFsdWVzUmV2ZXJzZWQoKT90LnN0YXJ0OnQuc3RhcnQrdC5sZW5ndGgscl19fSx0LnByb3RvdHlwZS5faXNDaGFyV29yZFNlcGFyYXRvcj1mdW5jdGlvbihlKXtyZXR1cm4gMCE9PWUuZ2V0V2lkdGgoKSYmdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy53b3JkU2VwYXJhdG9yLmluZGV4T2YoZS5nZXRDaGFycygpKT49MH0sdC5wcm90b3R5cGUuX3NlbGVjdExpbmVBdD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci5nZXRXcmFwcGVkUmFuZ2VGb3JMaW5lKGUpO3RoaXMuX21vZGVsLnNlbGVjdGlvblN0YXJ0PVswLHQuZmlyc3RdLHRoaXMuX21vZGVsLnNlbGVjdGlvbkVuZD1bdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLHQubGFzdF0sdGhpcy5fbW9kZWwuc2VsZWN0aW9uU3RhcnRMZW5ndGg9MH0sbyhbcygzLGYuSUJ1ZmZlclNlcnZpY2UpLHMoNCxmLklDb3JlU2VydmljZSkscyg1LGguSU1vdXNlU2VydmljZSkscyg2LGYuSU9wdGlvbnNTZXJ2aWNlKSxzKDcsaC5JUmVuZGVyU2VydmljZSldLHQpfShwLkRpc3Bvc2FibGUpO3QuU2VsZWN0aW9uU2VydmljZT1tfSw0NzI1OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5JQ2hhcmFjdGVySm9pbmVyU2VydmljZT10LklTb3VuZFNlcnZpY2U9dC5JU2VsZWN0aW9uU2VydmljZT10LklSZW5kZXJTZXJ2aWNlPXQuSU1vdXNlU2VydmljZT10LklDb3JlQnJvd3NlclNlcnZpY2U9dC5JQ2hhclNpemVTZXJ2aWNlPXZvaWQgMDt2YXIgaT1yKDgzNDMpO3QuSUNoYXJTaXplU2VydmljZT0oMCxpLmNyZWF0ZURlY29yYXRvcikoIkNoYXJTaXplU2VydmljZSIpLHQuSUNvcmVCcm93c2VyU2VydmljZT0oMCxpLmNyZWF0ZURlY29yYXRvcikoIkNvcmVCcm93c2VyU2VydmljZSIpLHQuSU1vdXNlU2VydmljZT0oMCxpLmNyZWF0ZURlY29yYXRvcikoIk1vdXNlU2VydmljZSIpLHQuSVJlbmRlclNlcnZpY2U9KDAsaS5jcmVhdGVEZWNvcmF0b3IpKCJSZW5kZXJTZXJ2aWNlIiksdC5JU2VsZWN0aW9uU2VydmljZT0oMCxpLmNyZWF0ZURlY29yYXRvcikoIlNlbGVjdGlvblNlcnZpY2UiKSx0LklTb3VuZFNlcnZpY2U9KDAsaS5jcmVhdGVEZWNvcmF0b3IpKCJTb3VuZFNlcnZpY2UiKSx0LklDaGFyYWN0ZXJKb2luZXJTZXJ2aWNlPSgwLGkuY3JlYXRlRGVjb3JhdG9yKSgiQ2hhcmFjdGVySm9pbmVyU2VydmljZSIpfSwzNTc6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Tb3VuZFNlcnZpY2U9dm9pZCAwO3ZhciBvPXIoMjU4NSkscz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fb3B0aW9uc1NlcnZpY2U9ZX1yZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGUsImF1ZGlvQ29udGV4dCIse2dldDpmdW5jdGlvbigpe2lmKCFlLl9hdWRpb0NvbnRleHQpe3ZhciB0PXdpbmRvdy5BdWRpb0NvbnRleHR8fHdpbmRvdy53ZWJraXRBdWRpb0NvbnRleHQ7aWYoIXQpcmV0dXJuIGNvbnNvbGUud2FybigiV2ViIEF1ZGlvIEFQSSBpcyBub3Qgc3VwcG9ydGVkIGJ5IHRoaXMgYnJvd3Nlci4gQ29uc2lkZXIgdXBncmFkaW5nIHRvIHRoZSBsYXRlc3QgdmVyc2lvbiIpLG51bGw7ZS5fYXVkaW9Db250ZXh0PW5ldyB0fXJldHVybiBlLl9hdWRpb0NvbnRleHR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUucGxheUJlbGxTb3VuZD1mdW5jdGlvbigpe3ZhciB0PWUuYXVkaW9Db250ZXh0O2lmKHQpe3ZhciByPXQuY3JlYXRlQnVmZmVyU291cmNlKCk7dC5kZWNvZGVBdWRpb0RhdGEodGhpcy5fYmFzZTY0VG9BcnJheUJ1ZmZlcih0aGlzLl9yZW1vdmVNaW1lVHlwZSh0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmJlbGxTb3VuZCkpLChmdW5jdGlvbihlKXtyLmJ1ZmZlcj1lLHIuY29ubmVjdCh0LmRlc3RpbmF0aW9uKSxyLnN0YXJ0KDApfSkpfX0sZS5wcm90b3R5cGUuX2Jhc2U2NFRvQXJyYXlCdWZmZXI9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PXdpbmRvdy5hdG9iKGUpLHI9dC5sZW5ndGgsaT1uZXcgVWludDhBcnJheShyKSxuPTA7bjxyO24rKylpW25dPXQuY2hhckNvZGVBdChuKTtyZXR1cm4gaS5idWZmZXJ9LGUucHJvdG90eXBlLl9yZW1vdmVNaW1lVHlwZT1mdW5jdGlvbihlKXtyZXR1cm4gZS5zcGxpdCgiLCIpWzFdfSxlPWkoW24oMCxvLklPcHRpb25zU2VydmljZSldLGUpfSgpO3QuU291bmRTZXJ2aWNlPXN9LDYzNDk6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkNpcmN1bGFyTGlzdD12b2lkIDA7dmFyIGk9cig4NDYwKSxuPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9tYXhMZW5ndGg9ZSx0aGlzLm9uRGVsZXRlRW1pdHRlcj1uZXcgaS5FdmVudEVtaXR0ZXIsdGhpcy5vbkluc2VydEVtaXR0ZXI9bmV3IGkuRXZlbnRFbWl0dGVyLHRoaXMub25UcmltRW1pdHRlcj1uZXcgaS5FdmVudEVtaXR0ZXIsdGhpcy5fYXJyYXk9bmV3IEFycmF5KHRoaXMuX21heExlbmd0aCksdGhpcy5fc3RhcnRJbmRleD0wLHRoaXMuX2xlbmd0aD0wfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uRGVsZXRlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMub25EZWxldGVFbWl0dGVyLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25JbnNlcnQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5vbkluc2VydEVtaXR0ZXIuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvblRyaW0iLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5vblRyaW1FbWl0dGVyLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwibWF4TGVuZ3RoIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX21heExlbmd0aH0sc2V0OmZ1bmN0aW9uKGUpe2lmKHRoaXMuX21heExlbmd0aCE9PWUpe2Zvcih2YXIgdD1uZXcgQXJyYXkoZSkscj0wO3I8TWF0aC5taW4oZSx0aGlzLmxlbmd0aCk7cisrKXRbcl09dGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgocildO3RoaXMuX2FycmF5PXQsdGhpcy5fbWF4TGVuZ3RoPWUsdGhpcy5fc3RhcnRJbmRleD0wfX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImxlbmd0aCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9sZW5ndGh9LHNldDpmdW5jdGlvbihlKXtpZihlPnRoaXMuX2xlbmd0aClmb3IodmFyIHQ9dGhpcy5fbGVuZ3RoO3Q8ZTt0KyspdGhpcy5fYXJyYXlbdF09dm9pZCAwO3RoaXMuX2xlbmd0aD1lfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgoZSldfSxlLnByb3RvdHlwZS5zZXQ9ZnVuY3Rpb24oZSx0KXt0aGlzLl9hcnJheVt0aGlzLl9nZXRDeWNsaWNJbmRleChlKV09dH0sZS5wcm90b3R5cGUucHVzaD1mdW5jdGlvbihlKXt0aGlzLl9hcnJheVt0aGlzLl9nZXRDeWNsaWNJbmRleCh0aGlzLl9sZW5ndGgpXT1lLHRoaXMuX2xlbmd0aD09PXRoaXMuX21heExlbmd0aD8odGhpcy5fc3RhcnRJbmRleD0rK3RoaXMuX3N0YXJ0SW5kZXgldGhpcy5fbWF4TGVuZ3RoLHRoaXMub25UcmltRW1pdHRlci5maXJlKDEpKTp0aGlzLl9sZW5ndGgrK30sZS5wcm90b3R5cGUucmVjeWNsZT1mdW5jdGlvbigpe2lmKHRoaXMuX2xlbmd0aCE9PXRoaXMuX21heExlbmd0aCl0aHJvdyBuZXcgRXJyb3IoIkNhbiBvbmx5IHJlY3ljbGUgd2hlbiB0aGUgYnVmZmVyIGlzIGZ1bGwiKTtyZXR1cm4gdGhpcy5fc3RhcnRJbmRleD0rK3RoaXMuX3N0YXJ0SW5kZXgldGhpcy5fbWF4TGVuZ3RoLHRoaXMub25UcmltRW1pdHRlci5maXJlKDEpLHRoaXMuX2FycmF5W3RoaXMuX2dldEN5Y2xpY0luZGV4KHRoaXMuX2xlbmd0aC0xKV19LE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiaXNGdWxsIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2xlbmd0aD09PXRoaXMuX21heExlbmd0aH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5wb3A9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgodGhpcy5fbGVuZ3RoLS0tMSldfSxlLnByb3RvdHlwZS5zcGxpY2U9ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHI9W10saT0yO2k8YXJndW1lbnRzLmxlbmd0aDtpKyspcltpLTJdPWFyZ3VtZW50c1tpXTtpZih0KXtmb3IodmFyIG49ZTtuPHRoaXMuX2xlbmd0aC10O24rKyl0aGlzLl9hcnJheVt0aGlzLl9nZXRDeWNsaWNJbmRleChuKV09dGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgobit0KV07dGhpcy5fbGVuZ3RoLT10LHRoaXMub25EZWxldGVFbWl0dGVyLmZpcmUoe2luZGV4OmUsYW1vdW50OnR9KX1mb3Iobj10aGlzLl9sZW5ndGgtMTtuPj1lO24tLSl0aGlzLl9hcnJheVt0aGlzLl9nZXRDeWNsaWNJbmRleChuK3IubGVuZ3RoKV09dGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgobildO2ZvcihuPTA7bjxyLmxlbmd0aDtuKyspdGhpcy5fYXJyYXlbdGhpcy5fZ2V0Q3ljbGljSW5kZXgoZStuKV09cltuXTtpZihyLmxlbmd0aCYmdGhpcy5vbkluc2VydEVtaXR0ZXIuZmlyZSh7aW5kZXg6ZSxhbW91bnQ6ci5sZW5ndGh9KSx0aGlzLl9sZW5ndGgrci5sZW5ndGg+dGhpcy5fbWF4TGVuZ3RoKXt2YXIgbz10aGlzLl9sZW5ndGgrci5sZW5ndGgtdGhpcy5fbWF4TGVuZ3RoO3RoaXMuX3N0YXJ0SW5kZXgrPW8sdGhpcy5fbGVuZ3RoPXRoaXMuX21heExlbmd0aCx0aGlzLm9uVHJpbUVtaXR0ZXIuZmlyZShvKX1lbHNlIHRoaXMuX2xlbmd0aCs9ci5sZW5ndGh9LGUucHJvdG90eXBlLnRyaW1TdGFydD1mdW5jdGlvbihlKXtlPnRoaXMuX2xlbmd0aCYmKGU9dGhpcy5fbGVuZ3RoKSx0aGlzLl9zdGFydEluZGV4Kz1lLHRoaXMuX2xlbmd0aC09ZSx0aGlzLm9uVHJpbUVtaXR0ZXIuZmlyZShlKX0sZS5wcm90b3R5cGUuc2hpZnRFbGVtZW50cz1mdW5jdGlvbihlLHQscil7aWYoISh0PD0wKSl7aWYoZTwwfHxlPj10aGlzLl9sZW5ndGgpdGhyb3cgbmV3IEVycm9yKCJzdGFydCBhcmd1bWVudCBvdXQgb2YgcmFuZ2UiKTtpZihlK3I8MCl0aHJvdyBuZXcgRXJyb3IoIkNhbm5vdCBzaGlmdCBlbGVtZW50cyBpbiBsaXN0IGJleW9uZCBpbmRleCAwIik7aWYocj4wKXtmb3IodmFyIGk9dC0xO2k+PTA7aS0tKXRoaXMuc2V0KGUraStyLHRoaXMuZ2V0KGUraSkpO3ZhciBuPWUrdCtyLXRoaXMuX2xlbmd0aDtpZihuPjApZm9yKHRoaXMuX2xlbmd0aCs9bjt0aGlzLl9sZW5ndGg+dGhpcy5fbWF4TGVuZ3RoOyl0aGlzLl9sZW5ndGgtLSx0aGlzLl9zdGFydEluZGV4KyssdGhpcy5vblRyaW1FbWl0dGVyLmZpcmUoMSl9ZWxzZSBmb3IoaT0wO2k8dDtpKyspdGhpcy5zZXQoZStpK3IsdGhpcy5nZXQoZStpKSl9fSxlLnByb3RvdHlwZS5fZ2V0Q3ljbGljSW5kZXg9ZnVuY3Rpb24oZSl7cmV0dXJuKHRoaXMuX3N0YXJ0SW5kZXgrZSkldGhpcy5fbWF4TGVuZ3RofSxlfSgpO3QuQ2lyY3VsYXJMaXN0PW59LDE0Mzk6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5jbG9uZT12b2lkIDAsdC5jbG9uZT1mdW5jdGlvbiBlKHQscil7aWYodm9pZCAwPT09ciYmKHI9NSksIm9iamVjdCIhPXR5cGVvZiB0KXJldHVybiB0O3ZhciBpPUFycmF5LmlzQXJyYXkodCk/W106e307Zm9yKHZhciBuIGluIHQpaVtuXT1yPD0xP3Rbbl06dFtuXSYmZSh0W25dLHItMSk7cmV0dXJuIGl9fSw4OTY5OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkNvcmVUZXJtaW5hbD12b2lkIDA7dmFyIG89cig4NDQpLHM9cigyNTg1KSxhPXIoNDM0OCksYz1yKDc4NjYpLGw9cig3NDQpLHU9cig3MzAyKSxoPXIoNjk3NSksZj1yKDg0NjApLF89cigxNzUzKSxkPXIoMzczMCkscD1yKDE0ODApLHY9cig3OTk0KSxnPXIoOTI4MikseT1yKDU0MzUpLG09cig1OTgxKSxiPSExLFM9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0KXt2YXIgcj1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIHIuX29uQmluYXJ5PW5ldyBmLkV2ZW50RW1pdHRlcixyLl9vbkRhdGE9bmV3IGYuRXZlbnRFbWl0dGVyLHIuX29uTGluZUZlZWQ9bmV3IGYuRXZlbnRFbWl0dGVyLHIuX29uUmVzaXplPW5ldyBmLkV2ZW50RW1pdHRlcixyLl9vblNjcm9sbD1uZXcgZi5FdmVudEVtaXR0ZXIsci5faW5zdGFudGlhdGlvblNlcnZpY2U9bmV3IGEuSW5zdGFudGlhdGlvblNlcnZpY2Usci5vcHRpb25zU2VydmljZT1uZXcgdS5PcHRpb25zU2VydmljZSh0KSxyLl9pbnN0YW50aWF0aW9uU2VydmljZS5zZXRTZXJ2aWNlKHMuSU9wdGlvbnNTZXJ2aWNlLHIub3B0aW9uc1NlcnZpY2UpLHIuX2J1ZmZlclNlcnZpY2U9ci5yZWdpc3RlcihyLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShsLkJ1ZmZlclNlcnZpY2UpKSxyLl9pbnN0YW50aWF0aW9uU2VydmljZS5zZXRTZXJ2aWNlKHMuSUJ1ZmZlclNlcnZpY2Usci5fYnVmZmVyU2VydmljZSksci5fbG9nU2VydmljZT1yLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShjLkxvZ1NlcnZpY2UpLHIuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2Uocy5JTG9nU2VydmljZSxyLl9sb2dTZXJ2aWNlKSxyLmNvcmVTZXJ2aWNlPXIucmVnaXN0ZXIoci5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2UoaC5Db3JlU2VydmljZSwoZnVuY3Rpb24oKXtyZXR1cm4gci5zY3JvbGxUb0JvdHRvbSgpfSkpKSxyLl9pbnN0YW50aWF0aW9uU2VydmljZS5zZXRTZXJ2aWNlKHMuSUNvcmVTZXJ2aWNlLHIuY29yZVNlcnZpY2UpLHIuY29yZU1vdXNlU2VydmljZT1yLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShfLkNvcmVNb3VzZVNlcnZpY2UpLHIuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2Uocy5JQ29yZU1vdXNlU2VydmljZSxyLmNvcmVNb3VzZVNlcnZpY2UpLHIuX2RpcnR5Um93U2VydmljZT1yLl9pbnN0YW50aWF0aW9uU2VydmljZS5jcmVhdGVJbnN0YW5jZShkLkRpcnR5Um93U2VydmljZSksci5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShzLklEaXJ0eVJvd1NlcnZpY2Usci5fZGlydHlSb3dTZXJ2aWNlKSxyLnVuaWNvZGVTZXJ2aWNlPXIuX2luc3RhbnRpYXRpb25TZXJ2aWNlLmNyZWF0ZUluc3RhbmNlKHAuVW5pY29kZVNlcnZpY2UpLHIuX2luc3RhbnRpYXRpb25TZXJ2aWNlLnNldFNlcnZpY2Uocy5JVW5pY29kZVNlcnZpY2Usci51bmljb2RlU2VydmljZSksci5fY2hhcnNldFNlcnZpY2U9ci5faW5zdGFudGlhdGlvblNlcnZpY2UuY3JlYXRlSW5zdGFuY2Uodi5DaGFyc2V0U2VydmljZSksci5faW5zdGFudGlhdGlvblNlcnZpY2Uuc2V0U2VydmljZShzLklDaGFyc2V0U2VydmljZSxyLl9jaGFyc2V0U2VydmljZSksci5faW5wdXRIYW5kbGVyPW5ldyB5LklucHV0SGFuZGxlcihyLl9idWZmZXJTZXJ2aWNlLHIuX2NoYXJzZXRTZXJ2aWNlLHIuY29yZVNlcnZpY2Usci5fZGlydHlSb3dTZXJ2aWNlLHIuX2xvZ1NlcnZpY2Usci5vcHRpb25zU2VydmljZSxyLmNvcmVNb3VzZVNlcnZpY2Usci51bmljb2RlU2VydmljZSksci5yZWdpc3RlcigoMCxmLmZvcndhcmRFdmVudCkoci5faW5wdXRIYW5kbGVyLm9uTGluZUZlZWQsci5fb25MaW5lRmVlZCkpLHIucmVnaXN0ZXIoci5faW5wdXRIYW5kbGVyKSxyLnJlZ2lzdGVyKCgwLGYuZm9yd2FyZEV2ZW50KShyLl9idWZmZXJTZXJ2aWNlLm9uUmVzaXplLHIuX29uUmVzaXplKSksci5yZWdpc3RlcigoMCxmLmZvcndhcmRFdmVudCkoci5jb3JlU2VydmljZS5vbkRhdGEsci5fb25EYXRhKSksci5yZWdpc3RlcigoMCxmLmZvcndhcmRFdmVudCkoci5jb3JlU2VydmljZS5vbkJpbmFyeSxyLl9vbkJpbmFyeSkpLHIucmVnaXN0ZXIoci5vcHRpb25zU2VydmljZS5vbk9wdGlvbkNoYW5nZSgoZnVuY3Rpb24oZSl7cmV0dXJuIHIuX3VwZGF0ZU9wdGlvbnMoZSl9KSkpLHIucmVnaXN0ZXIoci5fYnVmZmVyU2VydmljZS5vblNjcm9sbCgoZnVuY3Rpb24oZSl7ci5fb25TY3JvbGwuZmlyZSh7cG9zaXRpb246ci5fYnVmZmVyU2VydmljZS5idWZmZXIueWRpc3Asc291cmNlOjB9KSxyLl9kaXJ0eVJvd1NlcnZpY2UubWFya1JhbmdlRGlydHkoci5fYnVmZmVyU2VydmljZS5idWZmZXIuc2Nyb2xsVG9wLHIuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnNjcm9sbEJvdHRvbSl9KSkpLHIucmVnaXN0ZXIoci5faW5wdXRIYW5kbGVyLm9uU2Nyb2xsKChmdW5jdGlvbihlKXtyLl9vblNjcm9sbC5maXJlKHtwb3NpdGlvbjpyLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci55ZGlzcCxzb3VyY2U6MH0pLHIuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eShyLl9idWZmZXJTZXJ2aWNlLmJ1ZmZlci5zY3JvbGxUb3Asci5fYnVmZmVyU2VydmljZS5idWZmZXIuc2Nyb2xsQm90dG9tKX0pKSksci5fd3JpdGVCdWZmZXI9bmV3IG0uV3JpdGVCdWZmZXIoKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIHIuX2lucHV0SGFuZGxlci5wYXJzZShlLHQpfSkpLHJ9cmV0dXJuIG4odCxlKSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQmluYXJ5Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uQmluYXJ5LmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25EYXRhIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uRGF0YS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uTGluZUZlZWQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25MaW5lRmVlZC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uUmVzaXplIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uUmVzaXplLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25TY3JvbGwiLHtnZXQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzO3JldHVybiB0aGlzLl9vblNjcm9sbEFwaXx8KHRoaXMuX29uU2Nyb2xsQXBpPW5ldyBmLkV2ZW50RW1pdHRlcix0aGlzLnJlZ2lzdGVyKHRoaXMuX29uU2Nyb2xsLmV2ZW50KChmdW5jdGlvbih0KXt2YXIgcjtudWxsPT09KHI9ZS5fb25TY3JvbGxBcGkpfHx2b2lkIDA9PT1yfHxyLmZpcmUodC5wb3NpdGlvbil9KSkpKSx0aGlzLl9vblNjcm9sbEFwaS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsImNvbHMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwicm93cyIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3N9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJidWZmZXJzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyc30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9wdGlvbnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zfSxzZXQ6ZnVuY3Rpb24oZSl7Zm9yKHZhciB0IGluIGUpdGhpcy5vcHRpb25zU2VydmljZS5vcHRpb25zW3RdPWVbdF19LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3ZhciB0O3RoaXMuX2lzRGlzcG9zZWR8fChlLnByb3RvdHlwZS5kaXNwb3NlLmNhbGwodGhpcyksbnVsbD09PSh0PXRoaXMuX3dpbmRvd3NNb2RlKXx8dm9pZCAwPT09dHx8dC5kaXNwb3NlKCksdGhpcy5fd2luZG93c01vZGU9dm9pZCAwKX0sdC5wcm90b3R5cGUud3JpdGU9ZnVuY3Rpb24oZSx0KXt0aGlzLl93cml0ZUJ1ZmZlci53cml0ZShlLHQpfSx0LnByb3RvdHlwZS53cml0ZVN5bmM9ZnVuY3Rpb24oZSx0KXt0aGlzLl9sb2dTZXJ2aWNlLmxvZ0xldmVsPD1zLkxvZ0xldmVsRW51bS5XQVJOJiYhYiYmKHRoaXMuX2xvZ1NlcnZpY2Uud2Fybigid3JpdGVTeW5jIGlzIHVucmVsaWFibGUgYW5kIHdpbGwgYmUgcmVtb3ZlZCBzb29uLiIpLGI9ITApLHRoaXMuX3dyaXRlQnVmZmVyLndyaXRlU3luYyhlLHQpfSx0LnByb3RvdHlwZS5yZXNpemU9ZnVuY3Rpb24oZSx0KXtpc05hTihlKXx8aXNOYU4odCl8fChlPU1hdGgubWF4KGUsbC5NSU5JTVVNX0NPTFMpLHQ9TWF0aC5tYXgodCxsLk1JTklNVU1fUk9XUyksdGhpcy5fYnVmZmVyU2VydmljZS5yZXNpemUoZSx0KSl9LHQucHJvdG90eXBlLnNjcm9sbD1mdW5jdGlvbihlLHQpe3ZvaWQgMD09PXQmJih0PSExKSx0aGlzLl9idWZmZXJTZXJ2aWNlLnNjcm9sbChlLHQpfSx0LnByb3RvdHlwZS5zY3JvbGxMaW5lcz1mdW5jdGlvbihlLHQscil7dGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGxMaW5lcyhlLHQscil9LHQucHJvdG90eXBlLnNjcm9sbFBhZ2VzPWZ1bmN0aW9uKGUpe3RoaXMuX2J1ZmZlclNlcnZpY2Uuc2Nyb2xsUGFnZXMoZSl9LHQucHJvdG90eXBlLnNjcm9sbFRvVG9wPWZ1bmN0aW9uKCl7dGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGxUb1RvcCgpfSx0LnByb3RvdHlwZS5zY3JvbGxUb0JvdHRvbT1mdW5jdGlvbigpe3RoaXMuX2J1ZmZlclNlcnZpY2Uuc2Nyb2xsVG9Cb3R0b20oKX0sdC5wcm90b3R5cGUuc2Nyb2xsVG9MaW5lPWZ1bmN0aW9uKGUpe3RoaXMuX2J1ZmZlclNlcnZpY2Uuc2Nyb2xsVG9MaW5lKGUpfSx0LnByb3RvdHlwZS5yZWdpc3RlckVzY0hhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5faW5wdXRIYW5kbGVyLnJlZ2lzdGVyRXNjSGFuZGxlcihlLHQpfSx0LnByb3RvdHlwZS5yZWdpc3RlckRjc0hhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5faW5wdXRIYW5kbGVyLnJlZ2lzdGVyRGNzSGFuZGxlcihlLHQpfSx0LnByb3RvdHlwZS5yZWdpc3RlckNzaUhhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5faW5wdXRIYW5kbGVyLnJlZ2lzdGVyQ3NpSGFuZGxlcihlLHQpfSx0LnByb3RvdHlwZS5yZWdpc3Rlck9zY0hhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5faW5wdXRIYW5kbGVyLnJlZ2lzdGVyT3NjSGFuZGxlcihlLHQpfSx0LnByb3RvdHlwZS5fc2V0dXA9ZnVuY3Rpb24oKXt0aGlzLm9wdGlvbnNTZXJ2aWNlLm9wdGlvbnMud2luZG93c01vZGUmJnRoaXMuX2VuYWJsZVdpbmRvd3NNb2RlKCl9LHQucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5faW5wdXRIYW5kbGVyLnJlc2V0KCksdGhpcy5fYnVmZmVyU2VydmljZS5yZXNldCgpLHRoaXMuX2NoYXJzZXRTZXJ2aWNlLnJlc2V0KCksdGhpcy5jb3JlU2VydmljZS5yZXNldCgpLHRoaXMuY29yZU1vdXNlU2VydmljZS5yZXNldCgpfSx0LnByb3RvdHlwZS5fdXBkYXRlT3B0aW9ucz1mdW5jdGlvbihlKXt2YXIgdDtzd2l0Y2goZSl7Y2FzZSJzY3JvbGxiYWNrIjp0aGlzLmJ1ZmZlcnMucmVzaXplKHRoaXMuY29scyx0aGlzLnJvd3MpO2JyZWFrO2Nhc2Uid2luZG93c01vZGUiOnRoaXMub3B0aW9uc1NlcnZpY2Uub3B0aW9ucy53aW5kb3dzTW9kZT90aGlzLl9lbmFibGVXaW5kb3dzTW9kZSgpOihudWxsPT09KHQ9dGhpcy5fd2luZG93c01vZGUpfHx2b2lkIDA9PT10fHx0LmRpc3Bvc2UoKSx0aGlzLl93aW5kb3dzTW9kZT12b2lkIDApfX0sdC5wcm90b3R5cGUuX2VuYWJsZVdpbmRvd3NNb2RlPWZ1bmN0aW9uKCl7dmFyIGU9dGhpcztpZighdGhpcy5fd2luZG93c01vZGUpe3ZhciB0PVtdO3QucHVzaCh0aGlzLm9uTGluZUZlZWQoZy51cGRhdGVXaW5kb3dzTW9kZVdyYXBwZWRTdGF0ZS5iaW5kKG51bGwsdGhpcy5fYnVmZmVyU2VydmljZSkpKSx0LnB1c2godGhpcy5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJIIn0sKGZ1bmN0aW9uKCl7cmV0dXJuKDAsZy51cGRhdGVXaW5kb3dzTW9kZVdyYXBwZWRTdGF0ZSkoZS5fYnVmZmVyU2VydmljZSksITF9KSkpLHRoaXMuX3dpbmRvd3NNb2RlPXtkaXNwb3NlOmZ1bmN0aW9uKCl7Zm9yKHZhciBlPTAscj10O2U8ci5sZW5ndGg7ZSsrKXJbZV0uZGlzcG9zZSgpfX19fSx0fShvLkRpc3Bvc2FibGUpO3QuQ29yZVRlcm1pbmFsPVN9LDg0NjA6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5mb3J3YXJkRXZlbnQ9dC5FdmVudEVtaXR0ZXI9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX2xpc3RlbmVycz1bXSx0aGlzLl9kaXNwb3NlZD0hMX1yZXR1cm4gT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJldmVudCIse2dldDpmdW5jdGlvbigpe3ZhciBlPXRoaXM7cmV0dXJuIHRoaXMuX2V2ZW50fHwodGhpcy5fZXZlbnQ9ZnVuY3Rpb24odCl7cmV0dXJuIGUuX2xpc3RlbmVycy5wdXNoKHQpLHtkaXNwb3NlOmZ1bmN0aW9uKCl7aWYoIWUuX2Rpc3Bvc2VkKWZvcih2YXIgcj0wO3I8ZS5fbGlzdGVuZXJzLmxlbmd0aDtyKyspaWYoZS5fbGlzdGVuZXJzW3JdPT09dClyZXR1cm4gdm9pZCBlLl9saXN0ZW5lcnMuc3BsaWNlKHIsMSl9fX0pLHRoaXMuX2V2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmZpcmU9ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHI9W10saT0wO2k8dGhpcy5fbGlzdGVuZXJzLmxlbmd0aDtpKyspci5wdXNoKHRoaXMuX2xpc3RlbmVyc1tpXSk7Zm9yKGk9MDtpPHIubGVuZ3RoO2krKylyW2ldLmNhbGwodm9pZCAwLGUsdCl9LGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt0aGlzLl9saXN0ZW5lcnMmJih0aGlzLl9saXN0ZW5lcnMubGVuZ3RoPTApLHRoaXMuX2Rpc3Bvc2VkPSEwfSxlfSgpO3QuRXZlbnRFbWl0dGVyPXIsdC5mb3J3YXJkRXZlbnQ9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSgoZnVuY3Rpb24oZSl7cmV0dXJuIHQuZmlyZShlKX0pKX19LDU0MzU6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuSW5wdXRIYW5kbGVyPXQuV2luZG93c09wdGlvbnNSZXBvcnRUeXBlPXZvaWQgMDt2YXIgbyxzPXIoMjU4NCksYT1yKDcxMTYpLGM9cigyMDE1KSxsPXIoODQ0KSx1PXIoODI3MyksaD1yKDQ4MiksZj1yKDg0MzcpLF89cig4NDYwKSxkPXIoNjQzKSxwPXIoNTExKSx2PXIoMzczNCksZz1yKDI1ODUpLHk9cig2MjQyKSxtPXIoNjM1MSksYj1yKDU5NDEpLFM9eyIoIjowLCIpIjoxLCIqIjoyLCIrIjozLCItIjoxLCIuIjoyfSxDPTEzMTA3MjtmdW5jdGlvbiB3KGUsdCl7aWYoZT4yNClyZXR1cm4gdC5zZXRXaW5MaW5lc3x8ITE7c3dpdGNoKGUpe2Nhc2UgMTpyZXR1cm4hIXQucmVzdG9yZVdpbjtjYXNlIDI6cmV0dXJuISF0Lm1pbmltaXplV2luO2Nhc2UgMzpyZXR1cm4hIXQuc2V0V2luUG9zaXRpb247Y2FzZSA0OnJldHVybiEhdC5zZXRXaW5TaXplUGl4ZWxzO2Nhc2UgNTpyZXR1cm4hIXQucmFpc2VXaW47Y2FzZSA2OnJldHVybiEhdC5sb3dlcldpbjtjYXNlIDc6cmV0dXJuISF0LnJlZnJlc2hXaW47Y2FzZSA4OnJldHVybiEhdC5zZXRXaW5TaXplQ2hhcnM7Y2FzZSA5OnJldHVybiEhdC5tYXhpbWl6ZVdpbjtjYXNlIDEwOnJldHVybiEhdC5mdWxsc2NyZWVuV2luO2Nhc2UgMTE6cmV0dXJuISF0LmdldFdpblN0YXRlO2Nhc2UgMTM6cmV0dXJuISF0LmdldFdpblBvc2l0aW9uO2Nhc2UgMTQ6cmV0dXJuISF0LmdldFdpblNpemVQaXhlbHM7Y2FzZSAxNTpyZXR1cm4hIXQuZ2V0U2NyZWVuU2l6ZVBpeGVscztjYXNlIDE2OnJldHVybiEhdC5nZXRDZWxsU2l6ZVBpeGVscztjYXNlIDE4OnJldHVybiEhdC5nZXRXaW5TaXplQ2hhcnM7Y2FzZSAxOTpyZXR1cm4hIXQuZ2V0U2NyZWVuU2l6ZUNoYXJzO2Nhc2UgMjA6cmV0dXJuISF0LmdldEljb25UaXRsZTtjYXNlIDIxOnJldHVybiEhdC5nZXRXaW5UaXRsZTtjYXNlIDIyOnJldHVybiEhdC5wdXNoVGl0bGU7Y2FzZSAyMzpyZXR1cm4hIXQucG9wVGl0bGU7Y2FzZSAyNDpyZXR1cm4hIXQuc2V0V2luTGluZXN9cmV0dXJuITF9IWZ1bmN0aW9uKGUpe2VbZS5HRVRfV0lOX1NJWkVfUElYRUxTPTBdPSJHRVRfV0lOX1NJWkVfUElYRUxTIixlW2UuR0VUX0NFTExfU0laRV9QSVhFTFM9MV09IkdFVF9DRUxMX1NJWkVfUElYRUxTIn0obz10LldpbmRvd3NPcHRpb25zUmVwb3J0VHlwZXx8KHQuV2luZG93c09wdGlvbnNSZXBvcnRUeXBlPXt9KSk7dmFyIEw9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyLGkpe3RoaXMuX2J1ZmZlclNlcnZpY2U9ZSx0aGlzLl9jb3JlU2VydmljZT10LHRoaXMuX2xvZ1NlcnZpY2U9cix0aGlzLl9vcHRpb25zU2VydmljZT1pLHRoaXMuX2RhdGE9bmV3IFVpbnQzMkFycmF5KDApfXJldHVybiBlLnByb3RvdHlwZS5ob29rPWZ1bmN0aW9uKGUpe3RoaXMuX2RhdGE9bmV3IFVpbnQzMkFycmF5KDApfSxlLnByb3RvdHlwZS5wdXQ9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2RhdGE9KDAsdS5jb25jYXQpKHRoaXMuX2RhdGEsZS5zdWJhcnJheSh0LHIpKX0sZS5wcm90b3R5cGUudW5ob29rPWZ1bmN0aW9uKGUpe2lmKCFlKXJldHVybiB0aGlzLl9kYXRhPW5ldyBVaW50MzJBcnJheSgwKSwhMDt2YXIgdD0oMCxoLnV0ZjMyVG9TdHJpbmcpKHRoaXMuX2RhdGEpO3N3aXRjaCh0aGlzLl9kYXRhPW5ldyBVaW50MzJBcnJheSgwKSx0KXtjYXNlJyJxJzp0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKydQMSRyMCJxJytzLkMwLkVTQysiXFwiKTticmVhaztjYXNlJyJwJzp0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKydQMSRyNjE7MSJwJytzLkMwLkVTQysiXFwiKTticmVhaztjYXNlInIiOnZhciByPXRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLnNjcm9sbFRvcCsxKyI7IisodGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIuc2Nyb2xsQm90dG9tKzEpKyJyIjt0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKyJQMSRyIityK3MuQzAuRVNDKyJcXCIpO2JyZWFrO2Nhc2UibSI6dGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChzLkMwLkVTQysiUDEkcjBtIitzLkMwLkVTQysiXFwiKTticmVhaztjYXNlIiBxIjp2YXIgaT17YmxvY2s6Mix1bmRlcmxpbmU6NCxiYXI6Nn1bdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JTdHlsZV07aS09dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JCbGluaz8xOjAsdGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChzLkMwLkVTQysiUDEkciIraSsiIHEiK3MuQzAuRVNDKyJcXCIpO2JyZWFrO2RlZmF1bHQ6dGhpcy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBEQ1MgJHEgJXMiLHQpLHRoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIlAwJHIiK3MuQzAuRVNDKyJcXCIpfXJldHVybiEwfSxlfSgpLEU9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gdCh0LHIsaSxuLG8sbCx1LGQsdil7dm9pZCAwPT09diYmKHY9bmV3IGMuRXNjYXBlU2VxdWVuY2VQYXJzZXIpO3ZhciBnPWUuY2FsbCh0aGlzKXx8dGhpcztnLl9idWZmZXJTZXJ2aWNlPXQsZy5fY2hhcnNldFNlcnZpY2U9cixnLl9jb3JlU2VydmljZT1pLGcuX2RpcnR5Um93U2VydmljZT1uLGcuX2xvZ1NlcnZpY2U9byxnLl9vcHRpb25zU2VydmljZT1sLGcuX2NvcmVNb3VzZVNlcnZpY2U9dSxnLl91bmljb2RlU2VydmljZT1kLGcuX3BhcnNlcj12LGcuX3BhcnNlQnVmZmVyPW5ldyBVaW50MzJBcnJheSg0MDk2KSxnLl9zdHJpbmdEZWNvZGVyPW5ldyBoLlN0cmluZ1RvVXRmMzIsZy5fdXRmOERlY29kZXI9bmV3IGguVXRmOFRvVXRmMzIsZy5fd29ya0NlbGw9bmV3IHAuQ2VsbERhdGEsZy5fd2luZG93VGl0bGU9IiIsZy5faWNvbk5hbWU9IiIsZy5fd2luZG93VGl0bGVTdGFjaz1bXSxnLl9pY29uTmFtZVN0YWNrPVtdLGcuX2N1ckF0dHJEYXRhPWYuREVGQVVMVF9BVFRSX0RBVEEuY2xvbmUoKSxnLl9lcmFzZUF0dHJEYXRhSW50ZXJuYWw9Zi5ERUZBVUxUX0FUVFJfREFUQS5jbG9uZSgpLGcuX29uUmVxdWVzdEJlbGw9bmV3IF8uRXZlbnRFbWl0dGVyLGcuX29uUmVxdWVzdFJlZnJlc2hSb3dzPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vblJlcXVlc3RSZXNldD1uZXcgXy5FdmVudEVtaXR0ZXIsZy5fb25SZXF1ZXN0U2VuZEZvY3VzPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vblJlcXVlc3RTeW5jU2Nyb2xsQmFyPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vblJlcXVlc3RXaW5kb3dzT3B0aW9uc1JlcG9ydD1uZXcgXy5FdmVudEVtaXR0ZXIsZy5fb25BMTF5Q2hhcj1uZXcgXy5FdmVudEVtaXR0ZXIsZy5fb25BMTF5VGFiPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vbkN1cnNvck1vdmU9bmV3IF8uRXZlbnRFbWl0dGVyLGcuX29uTGluZUZlZWQ9bmV3IF8uRXZlbnRFbWl0dGVyLGcuX29uU2Nyb2xsPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vblRpdGxlQ2hhbmdlPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9vbkNvbG9yPW5ldyBfLkV2ZW50RW1pdHRlcixnLl9wYXJzZVN0YWNrPXtwYXVzZWQ6ITEsY3Vyc29yU3RhcnRYOjAsY3Vyc29yU3RhcnRZOjAsZGVjb2RlZExlbmd0aDowLHBvc2l0aW9uOjB9LGcuX3NwZWNpYWxDb2xvcnM9WzI1NiwyNTcsMjU4XSxnLnJlZ2lzdGVyKGcuX3BhcnNlciksZy5fYWN0aXZlQnVmZmVyPWcuX2J1ZmZlclNlcnZpY2UuYnVmZmVyLGcucmVnaXN0ZXIoZy5fYnVmZmVyU2VydmljZS5idWZmZXJzLm9uQnVmZmVyQWN0aXZhdGUoKGZ1bmN0aW9uKGUpe3JldHVybiBnLl9hY3RpdmVCdWZmZXI9ZS5hY3RpdmVCdWZmZXJ9KSkpLGcuX3BhcnNlci5zZXRDc2lIYW5kbGVyRmFsbGJhY2soKGZ1bmN0aW9uKGUsdCl7Zy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBDU0kgY29kZTogIix7aWRlbnRpZmllcjpnLl9wYXJzZXIuaWRlbnRUb1N0cmluZyhlKSxwYXJhbXM6dC50b0FycmF5KCl9KX0pKSxnLl9wYXJzZXIuc2V0RXNjSGFuZGxlckZhbGxiYWNrKChmdW5jdGlvbihlKXtnLl9sb2dTZXJ2aWNlLmRlYnVnKCJVbmtub3duIEVTQyBjb2RlOiAiLHtpZGVudGlmaWVyOmcuX3BhcnNlci5pZGVudFRvU3RyaW5nKGUpfSl9KSksZy5fcGFyc2VyLnNldEV4ZWN1dGVIYW5kbGVyRmFsbGJhY2soKGZ1bmN0aW9uKGUpe2cuX2xvZ1NlcnZpY2UuZGVidWcoIlVua25vd24gRVhFQ1VURSBjb2RlOiAiLHtjb2RlOmV9KX0pKSxnLl9wYXJzZXIuc2V0T3NjSGFuZGxlckZhbGxiYWNrKChmdW5jdGlvbihlLHQscil7Zy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBPU0MgY29kZTogIix7aWRlbnRpZmllcjplLGFjdGlvbjp0LGRhdGE6cn0pfSkpLGcuX3BhcnNlci5zZXREY3NIYW5kbGVyRmFsbGJhY2soKGZ1bmN0aW9uKGUsdCxyKXsiSE9PSyI9PT10JiYocj1yLnRvQXJyYXkoKSksZy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBEQ1MgY29kZTogIix7aWRlbnRpZmllcjpnLl9wYXJzZXIuaWRlbnRUb1N0cmluZyhlKSxhY3Rpb246dCxwYXlsb2FkOnJ9KX0pKSxnLl9wYXJzZXIuc2V0UHJpbnRIYW5kbGVyKChmdW5jdGlvbihlLHQscil7cmV0dXJuIGcucHJpbnQoZSx0LHIpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJAIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmluc2VydENoYXJzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiAiLGZpbmFsOiJAIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNjcm9sbExlZnQoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IkEifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuY3Vyc29yVXAoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiICIsZmluYWw6IkEifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc2Nyb2xsUmlnaHQoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IkIifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuY3Vyc29yRG93bihlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiQyJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5jdXJzb3JGb3J3YXJkKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJEIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvckJhY2t3YXJkKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJFIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvck5leHRMaW5lKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJGIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvclByZWNlZGluZ0xpbmUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IkcifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuY3Vyc29yQ2hhckFic29sdXRlKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJIIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvclBvc2l0aW9uKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJJIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmN1cnNvckZvcndhcmRUYWIoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IkoifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuZXJhc2VJbkRpc3BsYXkoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7cHJlZml4OiI/IixmaW5hbDoiSiJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5lcmFzZUluRGlzcGxheShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiSyJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5lcmFzZUluTGluZShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtwcmVmaXg6Ij8iLGZpbmFsOiJLIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmVyYXNlSW5MaW5lKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJMIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmluc2VydExpbmVzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJNIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRlbGV0ZUxpbmVzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJQIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRlbGV0ZUNoYXJzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJTIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNjcm9sbFVwKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJUIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNjcm9sbERvd24oZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6IlgifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuZXJhc2VDaGFycyhlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiWiJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5jdXJzb3JCYWNrd2FyZFRhYihlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiYCJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5jaGFyUG9zQWJzb2x1dGUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6ImEifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuaFBvc2l0aW9uUmVsYXRpdmUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6ImIifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcucmVwZWF0UHJlY2VkaW5nQ2hhcmFjdGVyKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJjIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNlbmREZXZpY2VBdHRyaWJ1dGVzUHJpbWFyeShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtwcmVmaXg6Ij4iLGZpbmFsOiJjIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNlbmREZXZpY2VBdHRyaWJ1dGVzU2Vjb25kYXJ5KGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJkIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmxpbmVQb3NBYnNvbHV0ZShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiZSJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy52UG9zaXRpb25SZWxhdGl2ZShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoiZiJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5oVlBvc2l0aW9uKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJnIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnRhYkNsZWFyKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJoIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldE1vZGUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7cHJlZml4OiI/IixmaW5hbDoiaCJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5zZXRNb2RlUHJpdmF0ZShlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoibCJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5yZXNldE1vZGUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7cHJlZml4OiI/IixmaW5hbDoibCJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5yZXNldE1vZGVQcml2YXRlKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJtIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmNoYXJBdHRyaWJ1dGVzKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJuIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRldmljZVN0YXR1cyhlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtwcmVmaXg6Ij8iLGZpbmFsOiJuIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRldmljZVN0YXR1c1ByaXZhdGUoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiISIsZmluYWw6InAifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc29mdFJlc2V0KGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiAiLGZpbmFsOiJxIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldEN1cnNvclN0eWxlKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJyIn0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldFNjcm9sbFJlZ2lvbihlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtmaW5hbDoicyJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5zYXZlQ3Vyc29yKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ZpbmFsOiJ0In0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLndpbmRvd09wdGlvbnMoZSl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyQ3NpSGFuZGxlcih7ZmluYWw6InUifSwoZnVuY3Rpb24oZSl7cmV0dXJuIGcucmVzdG9yZUN1cnNvcihlKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKHtpbnRlcm1lZGlhdGVzOiInIixmaW5hbDoifSJ9LChmdW5jdGlvbihlKXtyZXR1cm4gZy5pbnNlcnRDb2x1bW5zKGUpfSkpLGcuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiciLGZpbmFsOiJ+In0sKGZ1bmN0aW9uKGUpe3JldHVybiBnLmRlbGV0ZUNvbHVtbnMoZSl9KSksZy5fcGFyc2VyLnNldEV4ZWN1dGVIYW5kbGVyKHMuQzAuQkVMLChmdW5jdGlvbigpe3JldHVybiBnLmJlbGwoKX0pKSxnLl9wYXJzZXIuc2V0RXhlY3V0ZUhhbmRsZXIocy5DMC5MRiwoZnVuY3Rpb24oKXtyZXR1cm4gZy5saW5lRmVlZCgpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMwLlZULChmdW5jdGlvbigpe3JldHVybiBnLmxpbmVGZWVkKCl9KSksZy5fcGFyc2VyLnNldEV4ZWN1dGVIYW5kbGVyKHMuQzAuRkYsKGZ1bmN0aW9uKCl7cmV0dXJuIGcubGluZUZlZWQoKX0pKSxnLl9wYXJzZXIuc2V0RXhlY3V0ZUhhbmRsZXIocy5DMC5DUiwoZnVuY3Rpb24oKXtyZXR1cm4gZy5jYXJyaWFnZVJldHVybigpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMwLkJTLChmdW5jdGlvbigpe3JldHVybiBnLmJhY2tzcGFjZSgpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMwLkhULChmdW5jdGlvbigpe3JldHVybiBnLnRhYigpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMwLlNPLChmdW5jdGlvbigpe3JldHVybiBnLnNoaWZ0T3V0KCl9KSksZy5fcGFyc2VyLnNldEV4ZWN1dGVIYW5kbGVyKHMuQzAuU0ksKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2hpZnRJbigpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMxLklORCwoZnVuY3Rpb24oKXtyZXR1cm4gZy5pbmRleCgpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMxLk5FTCwoZnVuY3Rpb24oKXtyZXR1cm4gZy5uZXh0TGluZSgpfSkpLGcuX3BhcnNlci5zZXRFeGVjdXRlSGFuZGxlcihzLkMxLkhUUywoZnVuY3Rpb24oKXtyZXR1cm4gZy50YWJTZXQoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKDAsbmV3IHkuT3NjSGFuZGxlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc2V0VGl0bGUoZSksZy5zZXRJY29uTmFtZShlKSwhMH0pKSksZy5fcGFyc2VyLnJlZ2lzdGVyT3NjSGFuZGxlcigxLG5ldyB5Lk9zY0hhbmRsZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldEljb25OYW1lKGUpfSkpKSxnLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKDIsbmV3IHkuT3NjSGFuZGxlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc2V0VGl0bGUoZSl9KSkpLGcuX3BhcnNlci5yZWdpc3Rlck9zY0hhbmRsZXIoNCxuZXcgeS5Pc2NIYW5kbGVyKChmdW5jdGlvbihlKXtyZXR1cm4gZy5zZXRPclJlcG9ydEluZGV4ZWRDb2xvcihlKX0pKSksZy5fcGFyc2VyLnJlZ2lzdGVyT3NjSGFuZGxlcigxMCxuZXcgeS5Pc2NIYW5kbGVyKChmdW5jdGlvbihlKXtyZXR1cm4gZy5zZXRPclJlcG9ydEZnQ29sb3IoZSl9KSkpLGcuX3BhcnNlci5yZWdpc3Rlck9zY0hhbmRsZXIoMTEsbmV3IHkuT3NjSGFuZGxlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGcuc2V0T3JSZXBvcnRCZ0NvbG9yKGUpfSkpKSxnLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKDEyLG5ldyB5Lk9zY0hhbmRsZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBnLnNldE9yUmVwb3J0Q3Vyc29yQ29sb3IoZSl9KSkpLGcuX3BhcnNlci5yZWdpc3Rlck9zY0hhbmRsZXIoMTA0LG5ldyB5Lk9zY0hhbmRsZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBnLnJlc3RvcmVJbmRleGVkQ29sb3IoZSl9KSkpLGcuX3BhcnNlci5yZWdpc3Rlck9zY0hhbmRsZXIoMTEwLG5ldyB5Lk9zY0hhbmRsZXIoKGZ1bmN0aW9uKGUpe3JldHVybiBnLnJlc3RvcmVGZ0NvbG9yKGUpfSkpKSxnLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKDExMSxuZXcgeS5Pc2NIYW5kbGVyKChmdW5jdGlvbihlKXtyZXR1cm4gZy5yZXN0b3JlQmdDb2xvcihlKX0pKSksZy5fcGFyc2VyLnJlZ2lzdGVyT3NjSGFuZGxlcigxMTIsbmV3IHkuT3NjSGFuZGxlcigoZnVuY3Rpb24oZSl7cmV0dXJuIGcucmVzdG9yZUN1cnNvckNvbG9yKGUpfSkpKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiNyJ9LChmdW5jdGlvbigpe3JldHVybiBnLnNhdmVDdXJzb3IoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiOCJ9LChmdW5jdGlvbigpe3JldHVybiBnLnJlc3RvcmVDdXJzb3IoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiRCJ9LChmdW5jdGlvbigpe3JldHVybiBnLmluZGV4KCl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7ZmluYWw6IkUifSwoZnVuY3Rpb24oKXtyZXR1cm4gZy5uZXh0TGluZSgpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJIIn0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcudGFiU2V0KCl9KSksZy5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7ZmluYWw6Ik0ifSwoZnVuY3Rpb24oKXtyZXR1cm4gZy5yZXZlcnNlSW5kZXgoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiPSJ9LChmdW5jdGlvbigpe3JldHVybiBnLmtleXBhZEFwcGxpY2F0aW9uTW9kZSgpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiI+In0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcua2V5cGFkTnVtZXJpY01vZGUoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiYyJ9LChmdW5jdGlvbigpe3JldHVybiBnLmZ1bGxSZXNldCgpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJuIn0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDIpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJvIn0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDMpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJ8In0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDMpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJ9In0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDIpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ZpbmFsOiJ+In0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2V0Z0xldmVsKDEpfSkpLGcuX3BhcnNlci5yZWdpc3RlckVzY0hhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiUiLGZpbmFsOiJAIn0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0RGVmYXVsdENoYXJzZXQoKX0pKSxnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtpbnRlcm1lZGlhdGVzOiIlIixmaW5hbDoiRyJ9LChmdW5jdGlvbigpe3JldHVybiBnLnNlbGVjdERlZmF1bHRDaGFyc2V0KCl9KSk7dmFyIG09ZnVuY3Rpb24oZSl7Yi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiKCIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiKCIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiKSIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiKSIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiKiIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiKiIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiKyIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiKyIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiLSIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiLSIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiLiIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiLiIrZSl9KSksYi5fcGFyc2VyLnJlZ2lzdGVyRXNjSGFuZGxlcih7aW50ZXJtZWRpYXRlczoiLyIsZmluYWw6ZX0sKGZ1bmN0aW9uKCl7cmV0dXJuIGcuc2VsZWN0Q2hhcnNldCgiLyIrZSl9KSl9LGI9dGhpcztmb3IodmFyIFMgaW4gYS5DSEFSU0VUUyltKFMpO3JldHVybiBnLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKHtpbnRlcm1lZGlhdGVzOiIjIixmaW5hbDoiOCJ9LChmdW5jdGlvbigpe3JldHVybiBnLnNjcmVlbkFsaWdubWVudFBhdHRlcm4oKX0pKSxnLl9wYXJzZXIuc2V0RXJyb3JIYW5kbGVyKChmdW5jdGlvbihlKXtyZXR1cm4gZy5fbG9nU2VydmljZS5lcnJvcigiUGFyc2luZyBlcnJvcjogIixlKSxlfSkpLGcuX3BhcnNlci5yZWdpc3RlckRjc0hhbmRsZXIoe2ludGVybWVkaWF0ZXM6IiQiLGZpbmFsOiJxIn0sbmV3IEwoZy5fYnVmZmVyU2VydmljZSxnLl9jb3JlU2VydmljZSxnLl9sb2dTZXJ2aWNlLGcuX29wdGlvbnNTZXJ2aWNlKSksZ31yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0QmVsbCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RCZWxsLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25SZXF1ZXN0UmVmcmVzaFJvd3MiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZXF1ZXN0UmVmcmVzaFJvd3MuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblJlcXVlc3RSZXNldCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RSZXNldC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uUmVxdWVzdFNlbmRGb2N1cyIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RTZW5kRm9jdXMuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblJlcXVlc3RTeW5jU2Nyb2xsQmFyIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblJlcXVlc3RXaW5kb3dzT3B0aW9uc1JlcG9ydCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vblJlcXVlc3RXaW5kb3dzT3B0aW9uc1JlcG9ydC5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQTExeUNoYXIiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25BMTF5Q2hhci5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uQTExeVRhYiIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkExMXlUYWIuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkN1cnNvck1vdmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25DdXJzb3JNb3ZlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25MaW5lRmVlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkxpbmVGZWVkLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25TY3JvbGwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25TY3JvbGwuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvblRpdGxlQ2hhbmdlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uVGl0bGVDaGFuZ2UuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkNvbG9yIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uQ29sb3IuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe2UucHJvdG90eXBlLmRpc3Bvc2UuY2FsbCh0aGlzKX0sdC5wcm90b3R5cGUuX3ByZXNlcnZlU3RhY2s9ZnVuY3Rpb24oZSx0LHIsaSl7dGhpcy5fcGFyc2VTdGFjay5wYXVzZWQ9ITAsdGhpcy5fcGFyc2VTdGFjay5jdXJzb3JTdGFydFg9ZSx0aGlzLl9wYXJzZVN0YWNrLmN1cnNvclN0YXJ0WT10LHRoaXMuX3BhcnNlU3RhY2suZGVjb2RlZExlbmd0aD1yLHRoaXMuX3BhcnNlU3RhY2sucG9zaXRpb249aX0sdC5wcm90b3R5cGUuX2xvZ1Nsb3dSZXNvbHZpbmdBc3luYz1mdW5jdGlvbihlKXt0aGlzLl9sb2dTZXJ2aWNlLmxvZ0xldmVsPD1nLkxvZ0xldmVsRW51bS5XQVJOJiZQcm9taXNlLnJhY2UoW2UsbmV3IFByb21pc2UoKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIHNldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIHQoIiNTTE9XX1RJTUVPVVQiKX0pLDVlMyl9KSldKS5jYXRjaCgoZnVuY3Rpb24oZSl7aWYoIiNTTE9XX1RJTUVPVVQiIT09ZSl0aHJvdyBlO2NvbnNvbGUud2FybigiYXN5bmMgcGFyc2VyIGhhbmRsZXIgdGFraW5nIGxvbmdlciB0aGFuIDUwMDAgbXMiKX0pKX0sdC5wcm90b3R5cGUucGFyc2U9ZnVuY3Rpb24oZSx0KXt2YXIgcixpPXRoaXMuX2FjdGl2ZUJ1ZmZlci54LG49dGhpcy5fYWN0aXZlQnVmZmVyLnksbz0wLHM9dGhpcy5fcGFyc2VTdGFjay5wYXVzZWQ7aWYocyl7aWYocj10aGlzLl9wYXJzZXIucGFyc2UodGhpcy5fcGFyc2VCdWZmZXIsdGhpcy5fcGFyc2VTdGFjay5kZWNvZGVkTGVuZ3RoLHQpKXJldHVybiB0aGlzLl9sb2dTbG93UmVzb2x2aW5nQXN5bmMocikscjtpPXRoaXMuX3BhcnNlU3RhY2suY3Vyc29yU3RhcnRYLG49dGhpcy5fcGFyc2VTdGFjay5jdXJzb3JTdGFydFksdGhpcy5fcGFyc2VTdGFjay5wYXVzZWQ9ITEsZS5sZW5ndGg+QyYmKG89dGhpcy5fcGFyc2VTdGFjay5wb3NpdGlvbitDKX1pZih0aGlzLl9sb2dTZXJ2aWNlLmxvZ0xldmVsPD1nLkxvZ0xldmVsRW51bS5ERUJVRyYmdGhpcy5fbG9nU2VydmljZS5kZWJ1ZygicGFyc2luZyBkYXRhIisoInN0cmluZyI9PXR5cGVvZiBlPycgIicrZSsnIic6IiIpLCJzdHJpbmciPT10eXBlb2YgZT9lLnNwbGl0KCIiKS5tYXAoKGZ1bmN0aW9uKGUpe3JldHVybiBlLmNoYXJDb2RlQXQoMCl9KSk6ZSksdGhpcy5fcGFyc2VCdWZmZXIubGVuZ3RoPGUubGVuZ3RoJiZ0aGlzLl9wYXJzZUJ1ZmZlci5sZW5ndGg8QyYmKHRoaXMuX3BhcnNlQnVmZmVyPW5ldyBVaW50MzJBcnJheShNYXRoLm1pbihlLmxlbmd0aCxDKSkpLHN8fHRoaXMuX2RpcnR5Um93U2VydmljZS5jbGVhclJhbmdlKCksZS5sZW5ndGg+Qylmb3IodmFyIGE9bzthPGUubGVuZ3RoO2ErPUMpe3ZhciBjPWErQzxlLmxlbmd0aD9hK0M6ZS5sZW5ndGgsbD0ic3RyaW5nIj09dHlwZW9mIGU/dGhpcy5fc3RyaW5nRGVjb2Rlci5kZWNvZGUoZS5zdWJzdHJpbmcoYSxjKSx0aGlzLl9wYXJzZUJ1ZmZlcik6dGhpcy5fdXRmOERlY29kZXIuZGVjb2RlKGUuc3ViYXJyYXkoYSxjKSx0aGlzLl9wYXJzZUJ1ZmZlcik7aWYocj10aGlzLl9wYXJzZXIucGFyc2UodGhpcy5fcGFyc2VCdWZmZXIsbCkpcmV0dXJuIHRoaXMuX3ByZXNlcnZlU3RhY2soaSxuLGwsYSksdGhpcy5fbG9nU2xvd1Jlc29sdmluZ0FzeW5jKHIpLHJ9ZWxzZSBpZighcyYmKGw9InN0cmluZyI9PXR5cGVvZiBlP3RoaXMuX3N0cmluZ0RlY29kZXIuZGVjb2RlKGUsdGhpcy5fcGFyc2VCdWZmZXIpOnRoaXMuX3V0ZjhEZWNvZGVyLmRlY29kZShlLHRoaXMuX3BhcnNlQnVmZmVyKSxyPXRoaXMuX3BhcnNlci5wYXJzZSh0aGlzLl9wYXJzZUJ1ZmZlcixsKSkpcmV0dXJuIHRoaXMuX3ByZXNlcnZlU3RhY2soaSxuLGwsMCksdGhpcy5fbG9nU2xvd1Jlc29sdmluZ0FzeW5jKHIpLHI7dGhpcy5fYWN0aXZlQnVmZmVyLng9PT1pJiZ0aGlzLl9hY3RpdmVCdWZmZXIueT09PW58fHRoaXMuX29uQ3Vyc29yTW92ZS5maXJlKCksdGhpcy5fb25SZXF1ZXN0UmVmcmVzaFJvd3MuZmlyZSh0aGlzLl9kaXJ0eVJvd1NlcnZpY2Uuc3RhcnQsdGhpcy5fZGlydHlSb3dTZXJ2aWNlLmVuZCl9LHQucHJvdG90eXBlLnByaW50PWZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuLG89dGhpcy5fY2hhcnNldFNlcnZpY2UuY2hhcnNldCxzPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuc2NyZWVuUmVhZGVyTW9kZSxhPXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scyxjPXRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy53cmFwYXJvdW5kLGw9dGhpcy5fY29yZVNlcnZpY2UubW9kZXMuaW5zZXJ0TW9kZSx1PXRoaXMuX2N1ckF0dHJEYXRhLGY9dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkpO3RoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnkpLHRoaXMuX2FjdGl2ZUJ1ZmZlci54JiZyLXQ+MCYmMj09PWYuZ2V0V2lkdGgodGhpcy5fYWN0aXZlQnVmZmVyLngtMSkmJmYuc2V0Q2VsbEZyb21Db2RlUG9pbnQodGhpcy5fYWN0aXZlQnVmZmVyLngtMSwwLDEsdS5mZyx1LmJnLHUuZXh0ZW5kZWQpO2Zvcih2YXIgXz10O188cjsrK18pe2lmKGk9ZVtfXSxuPXRoaXMuX3VuaWNvZGVTZXJ2aWNlLndjd2lkdGgoaSksaTwxMjcmJm8pe3ZhciBwPW9bU3RyaW5nLmZyb21DaGFyQ29kZShpKV07cCYmKGk9cC5jaGFyQ29kZUF0KDApKX1pZihzJiZ0aGlzLl9vbkExMXlDaGFyLmZpcmUoKDAsaC5zdHJpbmdGcm9tQ29kZVBvaW50KShpKSksbnx8IXRoaXMuX2FjdGl2ZUJ1ZmZlci54KXtpZih0aGlzLl9hY3RpdmVCdWZmZXIueCtuLTE+PWEpaWYoYyl7Zm9yKDt0aGlzLl9hY3RpdmVCdWZmZXIueDxhOylmLnNldENlbGxGcm9tQ29kZVBvaW50KHRoaXMuX2FjdGl2ZUJ1ZmZlci54KyssMCwxLHUuZmcsdS5iZyx1LmV4dGVuZGVkKTt0aGlzLl9hY3RpdmVCdWZmZXIueD0wLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KyssdGhpcy5fYWN0aXZlQnVmZmVyLnk9PT10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tKzE/KHRoaXMuX2FjdGl2ZUJ1ZmZlci55LS0sdGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGwodGhpcy5fZXJhc2VBdHRyRGF0YSgpLCEwKSk6KHRoaXMuX2FjdGl2ZUJ1ZmZlci55Pj10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MmJih0aGlzLl9hY3RpdmVCdWZmZXIueT10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSksdGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkpLmlzV3JhcHBlZD0hMCksZj10aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuZ2V0KHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZSt0aGlzLl9hY3RpdmVCdWZmZXIueSl9ZWxzZSBpZih0aGlzLl9hY3RpdmVCdWZmZXIueD1hLTEsMj09PW4pY29udGludWU7aWYobCYmKGYuaW5zZXJ0Q2VsbHModGhpcy5fYWN0aXZlQnVmZmVyLngsbix0aGlzLl9hY3RpdmVCdWZmZXIuZ2V0TnVsbENlbGwodSksdSksMj09PWYuZ2V0V2lkdGgoYS0xKSYmZi5zZXRDZWxsRnJvbUNvZGVQb2ludChhLTEsZC5OVUxMX0NFTExfQ09ERSxkLk5VTExfQ0VMTF9XSURUSCx1LmZnLHUuYmcsdS5leHRlbmRlZCkpLGYuc2V0Q2VsbEZyb21Db2RlUG9pbnQodGhpcy5fYWN0aXZlQnVmZmVyLngrKyxpLG4sdS5mZyx1LmJnLHUuZXh0ZW5kZWQpLG4+MClmb3IoOy0tbjspZi5zZXRDZWxsRnJvbUNvZGVQb2ludCh0aGlzLl9hY3RpdmVCdWZmZXIueCsrLDAsMCx1LmZnLHUuYmcsdS5leHRlbmRlZCl9ZWxzZSBmLmdldFdpZHRoKHRoaXMuX2FjdGl2ZUJ1ZmZlci54LTEpP2YuYWRkQ29kZXBvaW50VG9DZWxsKHRoaXMuX2FjdGl2ZUJ1ZmZlci54LTEsaSk6Zi5hZGRDb2RlcG9pbnRUb0NlbGwodGhpcy5fYWN0aXZlQnVmZmVyLngtMixpKX1yLXQ+MCYmKGYubG9hZENlbGwodGhpcy5fYWN0aXZlQnVmZmVyLngtMSx0aGlzLl93b3JrQ2VsbCksMj09PXRoaXMuX3dvcmtDZWxsLmdldFdpZHRoKCl8fHRoaXMuX3dvcmtDZWxsLmdldENvZGUoKT42NTUzNT90aGlzLl9wYXJzZXIucHJlY2VkaW5nQ29kZXBvaW50PTA6dGhpcy5fd29ya0NlbGwuaXNDb21iaW5lZCgpP3RoaXMuX3BhcnNlci5wcmVjZWRpbmdDb2RlcG9pbnQ9dGhpcy5fd29ya0NlbGwuZ2V0Q2hhcnMoKS5jaGFyQ29kZUF0KDApOnRoaXMuX3BhcnNlci5wcmVjZWRpbmdDb2RlcG9pbnQ9dGhpcy5fd29ya0NlbGwuY29udGVudCksdGhpcy5fYWN0aXZlQnVmZmVyLng8YSYmci10PjAmJjA9PT1mLmdldFdpZHRoKHRoaXMuX2FjdGl2ZUJ1ZmZlci54KSYmIWYuaGFzQ29udGVudCh0aGlzLl9hY3RpdmVCdWZmZXIueCkmJmYuc2V0Q2VsbEZyb21Db2RlUG9pbnQodGhpcy5fYWN0aXZlQnVmZmVyLngsMCwxLHUuZmcsdS5iZyx1LmV4dGVuZGVkKSx0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci55KX0sdC5wcm90b3R5cGUucmVnaXN0ZXJDc2lIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcztyZXR1cm4idCIhPT1lLmZpbmFsfHxlLnByZWZpeHx8ZS5pbnRlcm1lZGlhdGVzP3RoaXMuX3BhcnNlci5yZWdpc3RlckNzaUhhbmRsZXIoZSx0KTp0aGlzLl9wYXJzZXIucmVnaXN0ZXJDc2lIYW5kbGVyKGUsKGZ1bmN0aW9uKGUpe3JldHVybiF3KGUucGFyYW1zWzBdLHIuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMud2luZG93T3B0aW9ucyl8fHQoZSl9KSl9LHQucHJvdG90eXBlLnJlZ2lzdGVyRGNzSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9wYXJzZXIucmVnaXN0ZXJEY3NIYW5kbGVyKGUsbmV3IG0uRGNzSGFuZGxlcih0KSl9LHQucHJvdG90eXBlLnJlZ2lzdGVyRXNjSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9wYXJzZXIucmVnaXN0ZXJFc2NIYW5kbGVyKGUsdCl9LHQucHJvdG90eXBlLnJlZ2lzdGVyT3NjSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9wYXJzZXIucmVnaXN0ZXJPc2NIYW5kbGVyKGUsbmV3IHkuT3NjSGFuZGxlcih0KSl9LHQucHJvdG90eXBlLmJlbGw9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25SZXF1ZXN0QmVsbC5maXJlKCksITB9LHQucHJvdG90eXBlLmxpbmVGZWVkPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnkpLHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY29udmVydEVvbCYmKHRoaXMuX2FjdGl2ZUJ1ZmZlci54PTApLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KyssdGhpcy5fYWN0aXZlQnVmZmVyLnk9PT10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tKzE/KHRoaXMuX2FjdGl2ZUJ1ZmZlci55LS0sdGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGwodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSk6dGhpcy5fYWN0aXZlQnVmZmVyLnk+PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyYmKHRoaXMuX2FjdGl2ZUJ1ZmZlci55PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xKSx0aGlzLl9hY3RpdmVCdWZmZXIueD49dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzJiZ0aGlzLl9hY3RpdmVCdWZmZXIueC0tLHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnkpLHRoaXMuX29uTGluZUZlZWQuZmlyZSgpLCEwfSx0LnByb3RvdHlwZS5jYXJyaWFnZVJldHVybj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9hY3RpdmVCdWZmZXIueD0wLCEwfSx0LnByb3RvdHlwZS5iYWNrc3BhY2U9ZnVuY3Rpb24oKXt2YXIgZTtpZighdGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLnJldmVyc2VXcmFwYXJvdW5kKXJldHVybiB0aGlzLl9yZXN0cmljdEN1cnNvcigpLHRoaXMuX2FjdGl2ZUJ1ZmZlci54PjAmJnRoaXMuX2FjdGl2ZUJ1ZmZlci54LS0sITA7aWYodGhpcy5fcmVzdHJpY3RDdXJzb3IodGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSx0aGlzLl9hY3RpdmVCdWZmZXIueD4wKXRoaXMuX2FjdGl2ZUJ1ZmZlci54LS07ZWxzZSBpZigwPT09dGhpcy5fYWN0aXZlQnVmZmVyLngmJnRoaXMuX2FjdGl2ZUJ1ZmZlci55PnRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3AmJnRoaXMuX2FjdGl2ZUJ1ZmZlci55PD10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tJiYobnVsbD09PShlPXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55KSl8fHZvaWQgMD09PWU/dm9pZCAwOmUuaXNXcmFwcGVkKSl7dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkpLmlzV3JhcHBlZD0hMSx0aGlzLl9hY3RpdmVCdWZmZXIueS0tLHRoaXMuX2FjdGl2ZUJ1ZmZlci54PXRoaXMuX2J1ZmZlclNlcnZpY2UuY29scy0xO3ZhciB0PXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55KTt0Lmhhc1dpZHRoKHRoaXMuX2FjdGl2ZUJ1ZmZlci54KSYmIXQuaGFzQ29udGVudCh0aGlzLl9hY3RpdmVCdWZmZXIueCkmJnRoaXMuX2FjdGl2ZUJ1ZmZlci54LS19cmV0dXJuIHRoaXMuX3Jlc3RyaWN0Q3Vyc29yKCksITB9LHQucHJvdG90eXBlLnRhYj1mdW5jdGlvbigpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci54Pj10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpcmV0dXJuITA7dmFyIGU9dGhpcy5fYWN0aXZlQnVmZmVyLng7cmV0dXJuIHRoaXMuX2FjdGl2ZUJ1ZmZlci54PXRoaXMuX2FjdGl2ZUJ1ZmZlci5uZXh0U3RvcCgpLHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuc2NyZWVuUmVhZGVyTW9kZSYmdGhpcy5fb25BMTF5VGFiLmZpcmUodGhpcy5fYWN0aXZlQnVmZmVyLngtZSksITB9LHQucHJvdG90eXBlLnNoaWZ0T3V0PWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NoYXJzZXRTZXJ2aWNlLnNldGdMZXZlbCgxKSwhMH0sdC5wcm90b3R5cGUuc2hpZnRJbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnTGV2ZWwoMCksITB9LHQucHJvdG90eXBlLl9yZXN0cmljdEN1cnNvcj1mdW5jdGlvbihlKXt2b2lkIDA9PT1lJiYoZT10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMtMSksdGhpcy5fYWN0aXZlQnVmZmVyLng9TWF0aC5taW4oZSxNYXRoLm1heCgwLHRoaXMuX2FjdGl2ZUJ1ZmZlci54KSksdGhpcy5fYWN0aXZlQnVmZmVyLnk9dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLm9yaWdpbj9NYXRoLm1pbih0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tLE1hdGgubWF4KHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3AsdGhpcy5fYWN0aXZlQnVmZmVyLnkpKTpNYXRoLm1pbih0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSxNYXRoLm1heCgwLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KSksdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIueSl9LHQucHJvdG90eXBlLl9zZXRDdXJzb3I9ZnVuY3Rpb24oZSx0KXt0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci55KSx0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMub3JpZ2luPyh0aGlzLl9hY3RpdmVCdWZmZXIueD1lLHRoaXMuX2FjdGl2ZUJ1ZmZlci55PXRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3ArdCk6KHRoaXMuX2FjdGl2ZUJ1ZmZlci54PWUsdGhpcy5fYWN0aXZlQnVmZmVyLnk9dCksdGhpcy5fcmVzdHJpY3RDdXJzb3IoKSx0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci55KX0sdC5wcm90b3R5cGUuX21vdmVDdXJzb3I9ZnVuY3Rpb24oZSx0KXt0aGlzLl9yZXN0cmljdEN1cnNvcigpLHRoaXMuX3NldEN1cnNvcih0aGlzLl9hY3RpdmVCdWZmZXIueCtlLHRoaXMuX2FjdGl2ZUJ1ZmZlci55K3QpfSx0LnByb3RvdHlwZS5jdXJzb3JVcD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9hY3RpdmVCdWZmZXIueS10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wO3JldHVybiB0Pj0wP3RoaXMuX21vdmVDdXJzb3IoMCwtTWF0aC5taW4odCxlLnBhcmFtc1swXXx8MSkpOnRoaXMuX21vdmVDdXJzb3IoMCwtKGUucGFyYW1zWzBdfHwxKSksITB9LHQucHJvdG90eXBlLmN1cnNvckRvd249ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbS10aGlzLl9hY3RpdmVCdWZmZXIueTtyZXR1cm4gdD49MD90aGlzLl9tb3ZlQ3Vyc29yKDAsTWF0aC5taW4odCxlLnBhcmFtc1swXXx8MSkpOnRoaXMuX21vdmVDdXJzb3IoMCxlLnBhcmFtc1swXXx8MSksITB9LHQucHJvdG90eXBlLmN1cnNvckZvcndhcmQ9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX21vdmVDdXJzb3IoZS5wYXJhbXNbMF18fDEsMCksITB9LHQucHJvdG90eXBlLmN1cnNvckJhY2t3YXJkPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9tb3ZlQ3Vyc29yKC0oZS5wYXJhbXNbMF18fDEpLDApLCEwfSx0LnByb3RvdHlwZS5jdXJzb3JOZXh0TGluZT1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5jdXJzb3JEb3duKGUpLHRoaXMuX2FjdGl2ZUJ1ZmZlci54PTAsITB9LHQucHJvdG90eXBlLmN1cnNvclByZWNlZGluZ0xpbmU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuY3Vyc29yVXAoZSksdGhpcy5fYWN0aXZlQnVmZmVyLng9MCwhMH0sdC5wcm90b3R5cGUuY3Vyc29yQ2hhckFic29sdXRlPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9zZXRDdXJzb3IoKGUucGFyYW1zWzBdfHwxKS0xLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KSwhMH0sdC5wcm90b3R5cGUuY3Vyc29yUG9zaXRpb249ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX3NldEN1cnNvcihlLmxlbmd0aD49Mj8oZS5wYXJhbXNbMV18fDEpLTE6MCwoZS5wYXJhbXNbMF18fDEpLTEpLCEwfSx0LnByb3RvdHlwZS5jaGFyUG9zQWJzb2x1dGU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX3NldEN1cnNvcigoZS5wYXJhbXNbMF18fDEpLTEsdGhpcy5fYWN0aXZlQnVmZmVyLnkpLCEwfSx0LnByb3RvdHlwZS5oUG9zaXRpb25SZWxhdGl2ZT1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fbW92ZUN1cnNvcihlLnBhcmFtc1swXXx8MSwwKSwhMH0sdC5wcm90b3R5cGUubGluZVBvc0Fic29sdXRlPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9zZXRDdXJzb3IodGhpcy5fYWN0aXZlQnVmZmVyLngsKGUucGFyYW1zWzBdfHwxKS0xKSwhMH0sdC5wcm90b3R5cGUudlBvc2l0aW9uUmVsYXRpdmU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX21vdmVDdXJzb3IoMCxlLnBhcmFtc1swXXx8MSksITB9LHQucHJvdG90eXBlLmhWUG9zaXRpb249ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuY3Vyc29yUG9zaXRpb24oZSksITB9LHQucHJvdG90eXBlLnRhYkNsZWFyPWZ1bmN0aW9uKGUpe3ZhciB0PWUucGFyYW1zWzBdO3JldHVybiAwPT09dD9kZWxldGUgdGhpcy5fYWN0aXZlQnVmZmVyLnRhYnNbdGhpcy5fYWN0aXZlQnVmZmVyLnhdOjM9PT10JiYodGhpcy5fYWN0aXZlQnVmZmVyLnRhYnM9e30pLCEwfSx0LnByb3RvdHlwZS5jdXJzb3JGb3J3YXJkVGFiPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci54Pj10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpcmV0dXJuITA7Zm9yKHZhciB0PWUucGFyYW1zWzBdfHwxO3QtLTspdGhpcy5fYWN0aXZlQnVmZmVyLng9dGhpcy5fYWN0aXZlQnVmZmVyLm5leHRTdG9wKCk7cmV0dXJuITB9LHQucHJvdG90eXBlLmN1cnNvckJhY2t3YXJkVGFiPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci54Pj10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpcmV0dXJuITA7Zm9yKHZhciB0PWUucGFyYW1zWzBdfHwxO3QtLTspdGhpcy5fYWN0aXZlQnVmZmVyLng9dGhpcy5fYWN0aXZlQnVmZmVyLnByZXZTdG9wKCk7cmV0dXJuITB9LHQucHJvdG90eXBlLl9lcmFzZUluQnVmZmVyTGluZT1mdW5jdGlvbihlLHQscixpKXt2b2lkIDA9PT1pJiYoaT0hMSk7dmFyIG49dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrZSk7bi5yZXBsYWNlQ2VsbHModCxyLHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaSYmKG4uaXNXcmFwcGVkPSExKX0sdC5wcm90b3R5cGUuX3Jlc2V0QnVmZmVyTGluZT1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuZ2V0KHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZStlKTt0LmZpbGwodGhpcy5fYWN0aXZlQnVmZmVyLmdldE51bGxDZWxsKHRoaXMuX2VyYXNlQXR0ckRhdGEoKSkpLHQuaXNXcmFwcGVkPSExfSx0LnByb3RvdHlwZS5lcmFzZUluRGlzcGxheT1mdW5jdGlvbihlKXt2YXIgdDtzd2l0Y2godGhpcy5fcmVzdHJpY3RDdXJzb3IodGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKSxlLnBhcmFtc1swXSl7Y2FzZSAwOmZvcih0PXRoaXMuX2FjdGl2ZUJ1ZmZlci55LHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodCksdGhpcy5fZXJhc2VJbkJ1ZmZlckxpbmUodCsrLHRoaXMuX2FjdGl2ZUJ1ZmZlci54LHRoaXMuX2J1ZmZlclNlcnZpY2UuY29scywwPT09dGhpcy5fYWN0aXZlQnVmZmVyLngpO3Q8dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzO3QrKyl0aGlzLl9yZXNldEJ1ZmZlckxpbmUodCk7dGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0KTticmVhaztjYXNlIDE6Zm9yKHQ9dGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0KSx0aGlzLl9lcmFzZUluQnVmZmVyTGluZSh0LDAsdGhpcy5fYWN0aXZlQnVmZmVyLngrMSwhMCksdGhpcy5fYWN0aXZlQnVmZmVyLngrMT49dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzJiYodGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0KzEpLmlzV3JhcHBlZD0hMSk7dC0tOyl0aGlzLl9yZXNldEJ1ZmZlckxpbmUodCk7dGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSgwKTticmVhaztjYXNlIDI6Zm9yKHQ9dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodC0xKTt0LS07KXRoaXMuX3Jlc2V0QnVmZmVyTGluZSh0KTt0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KDApO2JyZWFrO2Nhc2UgMzp2YXIgcj10aGlzLl9hY3RpdmVCdWZmZXIubGluZXMubGVuZ3RoLXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cztyPjAmJih0aGlzLl9hY3RpdmVCdWZmZXIubGluZXMudHJpbVN0YXJ0KHIpLHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZT1NYXRoLm1heCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UtciwwKSx0aGlzLl9hY3RpdmVCdWZmZXIueWRpc3A9TWF0aC5tYXgodGhpcy5fYWN0aXZlQnVmZmVyLnlkaXNwLXIsMCksdGhpcy5fb25TY3JvbGwuZmlyZSgwKSl9cmV0dXJuITB9LHQucHJvdG90eXBlLmVyYXNlSW5MaW5lPWZ1bmN0aW9uKGUpe3N3aXRjaCh0aGlzLl9yZXN0cmljdEN1cnNvcih0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMpLGUucGFyYW1zWzBdKXtjYXNlIDA6dGhpcy5fZXJhc2VJbkJ1ZmZlckxpbmUodGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fYWN0aXZlQnVmZmVyLngsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLDA9PT10aGlzLl9hY3RpdmVCdWZmZXIueCk7YnJlYWs7Y2FzZSAxOnRoaXMuX2VyYXNlSW5CdWZmZXJMaW5lKHRoaXMuX2FjdGl2ZUJ1ZmZlci55LDAsdGhpcy5fYWN0aXZlQnVmZmVyLngrMSwhMSk7YnJlYWs7Y2FzZSAyOnRoaXMuX2VyYXNlSW5CdWZmZXJMaW5lKHRoaXMuX2FjdGl2ZUJ1ZmZlci55LDAsdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzLCEwKX1yZXR1cm4gdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIueSksITB9LHQucHJvdG90eXBlLmluc2VydExpbmVzPWZ1bmN0aW9uKGUpe3RoaXMuX3Jlc3RyaWN0Q3Vyc29yKCk7dmFyIHQ9ZS5wYXJhbXNbMF18fDE7aWYodGhpcy5fYWN0aXZlQnVmZmVyLnk+dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbXx8dGhpcy5fYWN0aXZlQnVmZmVyLnk8dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcClyZXR1cm4hMDtmb3IodmFyIHI9dGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55LGk9dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEtdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSxuPXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xK3RoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZS1pKzE7dC0tOyl0aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuc3BsaWNlKG4tMSwxKSx0aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuc3BsaWNlKHIsMCx0aGlzLl9hY3RpdmVCdWZmZXIuZ2V0QmxhbmtMaW5lKHRoaXMuX2VyYXNlQXR0ckRhdGEoKSkpO3JldHVybiB0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya1JhbmdlRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSksdGhpcy5fYWN0aXZlQnVmZmVyLng9MCwhMH0sdC5wcm90b3R5cGUuZGVsZXRlTGluZXM9ZnVuY3Rpb24oZSl7dGhpcy5fcmVzdHJpY3RDdXJzb3IoKTt2YXIgdD1lLnBhcmFtc1swXXx8MTtpZih0aGlzLl9hY3RpdmVCdWZmZXIueT50aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tfHx0aGlzLl9hY3RpdmVCdWZmZXIueTx0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wKXJldHVybiEwO3ZhciByLGk9dGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55O2ZvcihyPXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xLXRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20scj10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSt0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2Utcjt0LS07KXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zcGxpY2UoaSwxKSx0aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuc3BsaWNlKHIsMCx0aGlzLl9hY3RpdmVCdWZmZXIuZ2V0QmxhbmtMaW5lKHRoaXMuX2VyYXNlQXR0ckRhdGEoKSkpO3JldHVybiB0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya1JhbmdlRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSksdGhpcy5fYWN0aXZlQnVmZmVyLng9MCwhMH0sdC5wcm90b3R5cGUuaW5zZXJ0Q2hhcnM9ZnVuY3Rpb24oZSl7dGhpcy5fcmVzdHJpY3RDdXJzb3IoKTt2YXIgdD10aGlzLl9hY3RpdmVCdWZmZXIubGluZXMuZ2V0KHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZSt0aGlzLl9hY3RpdmVCdWZmZXIueSk7cmV0dXJuIHQmJih0Lmluc2VydENlbGxzKHRoaXMuX2FjdGl2ZUJ1ZmZlci54LGUucGFyYW1zWzBdfHwxLHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIueSkpLCEwfSx0LnByb3RvdHlwZS5kZWxldGVDaGFycz1mdW5jdGlvbihlKXt0aGlzLl9yZXN0cmljdEN1cnNvcigpO3ZhciB0PXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55KTtyZXR1cm4gdCYmKHQuZGVsZXRlQ2VsbHModGhpcy5fYWN0aXZlQnVmZmVyLngsZS5wYXJhbXNbMF18fDEsdGhpcy5fYWN0aXZlQnVmZmVyLmdldE51bGxDZWxsKHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksdGhpcy5fZXJhc2VBdHRyRGF0YSgpKSx0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya0RpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci55KSksITB9LHQucHJvdG90eXBlLnNjcm9sbFVwPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MTt0LS07KXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zcGxpY2UodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3AsMSksdGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLnNwbGljZSh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSwwLHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXRCbGFua0xpbmUodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSk7cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5zY3JvbGxEb3duPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MTt0LS07KXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zcGxpY2UodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20sMSksdGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLnNwbGljZSh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcCwwLHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXRCbGFua0xpbmUoZi5ERUZBVUxUX0FUVFJfREFUQSkpO3JldHVybiB0aGlzLl9kaXJ0eVJvd1NlcnZpY2UubWFya1JhbmdlRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcCx0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tKSwhMH0sdC5wcm90b3R5cGUuc2Nyb2xsTGVmdD1mdW5jdGlvbihlKXtpZih0aGlzLl9hY3RpdmVCdWZmZXIueT50aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tfHx0aGlzLl9hY3RpdmVCdWZmZXIueTx0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wKXJldHVybiEwO2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MSxyPXRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3A7cjw9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbTsrK3Ipe3ZhciBpPXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3IpO2kuZGVsZXRlQ2VsbHMoMCx0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaS5pc1dyYXBwZWQ9ITF9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5zY3JvbGxSaWdodD1mdW5jdGlvbihlKXtpZih0aGlzLl9hY3RpdmVCdWZmZXIueT50aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tfHx0aGlzLl9hY3RpdmVCdWZmZXIueTx0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wKXJldHVybiEwO2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MSxyPXRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3A7cjw9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbTsrK3Ipe3ZhciBpPXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3IpO2kuaW5zZXJ0Q2VsbHMoMCx0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaS5pc1dyYXBwZWQ9ITF9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5pbnNlcnRDb2x1bW5zPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci55PnRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b218fHRoaXMuX2FjdGl2ZUJ1ZmZlci55PHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3ApcmV0dXJuITA7Zm9yKHZhciB0PWUucGFyYW1zWzBdfHwxLHI9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcDtyPD10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tOysrcil7dmFyIGk9dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2Urcik7aS5pbnNlcnRDZWxscyh0aGlzLl9hY3RpdmVCdWZmZXIueCx0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaS5pc1dyYXBwZWQ9ITF9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5kZWxldGVDb2x1bW5zPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2FjdGl2ZUJ1ZmZlci55PnRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b218fHRoaXMuX2FjdGl2ZUJ1ZmZlci55PHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3ApcmV0dXJuITA7Zm9yKHZhciB0PWUucGFyYW1zWzBdfHwxLHI9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcDtyPD10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tOysrcil7dmFyIGk9dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2Urcik7aS5kZWxldGVDZWxscyh0aGlzLl9hY3RpdmVCdWZmZXIueCx0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXROdWxsQ2VsbCh0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2VyYXNlQXR0ckRhdGEoKSksaS5pc1dyYXBwZWQ9ITF9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrUmFuZ2VEaXJ0eSh0aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b20pLCEwfSx0LnByb3RvdHlwZS5lcmFzZUNoYXJzPWZ1bmN0aW9uKGUpe3RoaXMuX3Jlc3RyaWN0Q3Vyc29yKCk7dmFyIHQ9dGhpcy5fYWN0aXZlQnVmZmVyLmxpbmVzLmdldCh0aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkpO3JldHVybiB0JiYodC5yZXBsYWNlQ2VsbHModGhpcy5fYWN0aXZlQnVmZmVyLngsdGhpcy5fYWN0aXZlQnVmZmVyLngrKGUucGFyYW1zWzBdfHwxKSx0aGlzLl9hY3RpdmVCdWZmZXIuZ2V0TnVsbENlbGwodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSx0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrRGlydHkodGhpcy5fYWN0aXZlQnVmZmVyLnkpKSwhMH0sdC5wcm90b3R5cGUucmVwZWF0UHJlY2VkaW5nQ2hhcmFjdGVyPWZ1bmN0aW9uKGUpe2lmKCF0aGlzLl9wYXJzZXIucHJlY2VkaW5nQ29kZXBvaW50KXJldHVybiEwO2Zvcih2YXIgdD1lLnBhcmFtc1swXXx8MSxyPW5ldyBVaW50MzJBcnJheSh0KSxpPTA7aTx0OysraSlyW2ldPXRoaXMuX3BhcnNlci5wcmVjZWRpbmdDb2RlcG9pbnQ7cmV0dXJuIHRoaXMucHJpbnQociwwLHIubGVuZ3RoKSwhMH0sdC5wcm90b3R5cGUuc2VuZERldmljZUF0dHJpYnV0ZXNQcmltYXJ5PWZ1bmN0aW9uKGUpe3JldHVybiBlLnBhcmFtc1swXT4wfHwodGhpcy5faXMoInh0ZXJtIil8fHRoaXMuX2lzKCJyeHZ0LXVuaWNvZGUiKXx8dGhpcy5faXMoInNjcmVlbiIpP3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIls/MTsyYyIpOnRoaXMuX2lzKCJsaW51eCIpJiZ0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKyJbPzZjIikpLCEwfSx0LnByb3RvdHlwZS5zZW5kRGV2aWNlQXR0cmlidXRlc1NlY29uZGFyeT1mdW5jdGlvbihlKXtyZXR1cm4gZS5wYXJhbXNbMF0+MHx8KHRoaXMuX2lzKCJ4dGVybSIpP3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIls+MDsyNzY7MGMiKTp0aGlzLl9pcygicnh2dC11bmljb2RlIik/dGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChzLkMwLkVTQysiWz44NTs5NTswYyIpOnRoaXMuX2lzKCJsaW51eCIpP3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQoZS5wYXJhbXNbMF0rImMiKTp0aGlzLl9pcygic2NyZWVuIikmJnRoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIls+ODM7NDAwMDM7MGMiKSksITB9LHQucHJvdG90eXBlLl9pcz1mdW5jdGlvbihlKXtyZXR1cm4gMD09PSh0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLnRlcm1OYW1lKyIiKS5pbmRleE9mKGUpfSx0LnByb3RvdHlwZS5zZXRNb2RlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8ZS5sZW5ndGg7dCsrKTQ9PT1lLnBhcmFtc1t0XSYmKHRoaXMuX2NvcmVTZXJ2aWNlLm1vZGVzLmluc2VydE1vZGU9ITApO3JldHVybiEwfSx0LnByb3RvdHlwZS5zZXRNb2RlUHJpdmF0ZT1mdW5jdGlvbihlKXtmb3IodmFyIHQ9MDt0PGUubGVuZ3RoO3QrKylzd2l0Y2goZS5wYXJhbXNbdF0pe2Nhc2UgMTp0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuYXBwbGljYXRpb25DdXJzb3JLZXlzPSEwO2JyZWFrO2Nhc2UgMjp0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnQ2hhcnNldCgwLGEuREVGQVVMVF9DSEFSU0VUKSx0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnQ2hhcnNldCgxLGEuREVGQVVMVF9DSEFSU0VUKSx0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnQ2hhcnNldCgyLGEuREVGQVVMVF9DSEFSU0VUKSx0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnQ2hhcnNldCgzLGEuREVGQVVMVF9DSEFSU0VUKTticmVhaztjYXNlIDM6dGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy53aW5kb3dPcHRpb25zLnNldFdpbkxpbmVzJiYodGhpcy5fYnVmZmVyU2VydmljZS5yZXNpemUoMTMyLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyksdGhpcy5fb25SZXF1ZXN0UmVzZXQuZmlyZSgpKTticmVhaztjYXNlIDY6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLm9yaWdpbj0hMCx0aGlzLl9zZXRDdXJzb3IoMCwwKTticmVhaztjYXNlIDc6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLndyYXBhcm91bmQ9ITA7YnJlYWs7Y2FzZSAxMjpicmVhaztjYXNlIDQ1OnRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5yZXZlcnNlV3JhcGFyb3VuZD0hMDticmVhaztjYXNlIDY2OnRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIlNlcmlhbCBwb3J0IHJlcXVlc3RlZCBhcHBsaWNhdGlvbiBrZXlwYWQuIiksdGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmFwcGxpY2F0aW9uS2V5cGFkPSEwLHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZmlyZSgpO2JyZWFrO2Nhc2UgOTp0aGlzLl9jb3JlTW91c2VTZXJ2aWNlLmFjdGl2ZVByb3RvY29sPSJYMTAiO2JyZWFrO2Nhc2UgMWUzOnRoaXMuX2NvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2w9IlZUMjAwIjticmVhaztjYXNlIDEwMDI6dGhpcy5fY29yZU1vdXNlU2VydmljZS5hY3RpdmVQcm90b2NvbD0iRFJBRyI7YnJlYWs7Y2FzZSAxMDAzOnRoaXMuX2NvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2w9IkFOWSI7YnJlYWs7Y2FzZSAxMDA0OnRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5zZW5kRm9jdXM9ITAsdGhpcy5fb25SZXF1ZXN0U2VuZEZvY3VzLmZpcmUoKTticmVhaztjYXNlIDEwMDU6dGhpcy5fbG9nU2VydmljZS5kZWJ1ZygiREVDU0VUIDEwMDUgbm90IHN1cHBvcnRlZCAoc2VlICMyNTA3KSIpO2JyZWFrO2Nhc2UgMTAwNjp0aGlzLl9jb3JlTW91c2VTZXJ2aWNlLmFjdGl2ZUVuY29kaW5nPSJTR1IiO2JyZWFrO2Nhc2UgMTAxNTp0aGlzLl9sb2dTZXJ2aWNlLmRlYnVnKCJERUNTRVQgMTAxNSBub3Qgc3VwcG9ydGVkIChzZWUgIzI1MDcpIik7YnJlYWs7Y2FzZSAyNTp0aGlzLl9jb3JlU2VydmljZS5pc0N1cnNvckhpZGRlbj0hMTticmVhaztjYXNlIDEwNDg6dGhpcy5zYXZlQ3Vyc29yKCk7YnJlYWs7Y2FzZSAxMDQ5OnRoaXMuc2F2ZUN1cnNvcigpO2Nhc2UgNDc6Y2FzZSAxMDQ3OnRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVycy5hY3RpdmF0ZUFsdEJ1ZmZlcih0aGlzLl9lcmFzZUF0dHJEYXRhKCkpLHRoaXMuX2NvcmVTZXJ2aWNlLmlzQ3Vyc29ySW5pdGlhbGl6ZWQ9ITAsdGhpcy5fb25SZXF1ZXN0UmVmcmVzaFJvd3MuZmlyZSgwLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xKSx0aGlzLl9vblJlcXVlc3RTeW5jU2Nyb2xsQmFyLmZpcmUoKTticmVhaztjYXNlIDIwMDQ6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmJyYWNrZXRlZFBhc3RlTW9kZT0hMH1yZXR1cm4hMH0sdC5wcm90b3R5cGUucmVzZXRNb2RlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8ZS5sZW5ndGg7dCsrKTQ9PT1lLnBhcmFtc1t0XSYmKHRoaXMuX2NvcmVTZXJ2aWNlLm1vZGVzLmluc2VydE1vZGU9ITEpO3JldHVybiEwfSx0LnByb3RvdHlwZS5yZXNldE1vZGVQcml2YXRlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD0wO3Q8ZS5sZW5ndGg7dCsrKXN3aXRjaChlLnBhcmFtc1t0XSl7Y2FzZSAxOnRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5hcHBsaWNhdGlvbkN1cnNvcktleXM9ITE7YnJlYWs7Y2FzZSAzOnRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMud2luZG93T3B0aW9ucy5zZXRXaW5MaW5lcyYmKHRoaXMuX2J1ZmZlclNlcnZpY2UucmVzaXplKDgwLHRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyksdGhpcy5fb25SZXF1ZXN0UmVzZXQuZmlyZSgpKTticmVhaztjYXNlIDY6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLm9yaWdpbj0hMSx0aGlzLl9zZXRDdXJzb3IoMCwwKTticmVhaztjYXNlIDc6dGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLndyYXBhcm91bmQ9ITE7YnJlYWs7Y2FzZSAxMjpicmVhaztjYXNlIDQ1OnRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5yZXZlcnNlV3JhcGFyb3VuZD0hMTticmVhaztjYXNlIDY2OnRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIlN3aXRjaGluZyBiYWNrIHRvIG5vcm1hbCBrZXlwYWQuIiksdGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmFwcGxpY2F0aW9uS2V5cGFkPSExLHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZmlyZSgpO2JyZWFrO2Nhc2UgOTpjYXNlIDFlMzpjYXNlIDEwMDI6Y2FzZSAxMDAzOnRoaXMuX2NvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2w9Ik5PTkUiO2JyZWFrO2Nhc2UgMTAwNDp0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuc2VuZEZvY3VzPSExO2JyZWFrO2Nhc2UgMTAwNTp0aGlzLl9sb2dTZXJ2aWNlLmRlYnVnKCJERUNSU1QgMTAwNSBub3Qgc3VwcG9ydGVkIChzZWUgIzI1MDcpIik7YnJlYWs7Y2FzZSAxMDA2OnRoaXMuX2NvcmVNb3VzZVNlcnZpY2UuYWN0aXZlRW5jb2Rpbmc9IkRFRkFVTFQiO2JyZWFrO2Nhc2UgMTAxNTp0aGlzLl9sb2dTZXJ2aWNlLmRlYnVnKCJERUNSU1QgMTAxNSBub3Qgc3VwcG9ydGVkIChzZWUgIzI1MDcpIik7YnJlYWs7Y2FzZSAyNTp0aGlzLl9jb3JlU2VydmljZS5pc0N1cnNvckhpZGRlbj0hMDticmVhaztjYXNlIDEwNDg6dGhpcy5yZXN0b3JlQ3Vyc29yKCk7YnJlYWs7Y2FzZSAxMDQ5OmNhc2UgNDc6Y2FzZSAxMDQ3OnRoaXMuX2J1ZmZlclNlcnZpY2UuYnVmZmVycy5hY3RpdmF0ZU5vcm1hbEJ1ZmZlcigpLDEwNDk9PT1lLnBhcmFtc1t0XSYmdGhpcy5yZXN0b3JlQ3Vyc29yKCksdGhpcy5fY29yZVNlcnZpY2UuaXNDdXJzb3JJbml0aWFsaXplZD0hMCx0aGlzLl9vblJlcXVlc3RSZWZyZXNoUm93cy5maXJlKDAsdGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEpLHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZmlyZSgpO2JyZWFrO2Nhc2UgMjAwNDp0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuYnJhY2tldGVkUGFzdGVNb2RlPSExfXJldHVybiEwfSx0LnByb3RvdHlwZS5fdXBkYXRlQXR0ckNvbG9yPWZ1bmN0aW9uKGUsdCxyLGksbil7cmV0dXJuIDI9PT10PyhlfD01MDMzMTY0OCxlJj0tMTY3NzcyMTYsZXw9di5BdHRyaWJ1dGVEYXRhLmZyb21Db2xvclJHQihbcixpLG5dKSk6NT09PXQmJihlJj0tNTAzMzE5MDQsZXw9MzM1NTQ0MzJ8MjU1JnIpLGV9LHQucHJvdG90eXBlLl9leHRyYWN0Q29sb3I9ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPVswLDAsLTEsMCwwLDBdLG49MCxvPTA7ZG97aWYoaVtvK25dPWUucGFyYW1zW3Qrb10sZS5oYXNTdWJQYXJhbXModCtvKSl7dmFyIHM9ZS5nZXRTdWJQYXJhbXModCtvKSxhPTA7ZG97NT09PWlbMV0mJihuPTEpLGlbbythKzErbl09c1thXX13aGlsZSgrK2E8cy5sZW5ndGgmJmErbysxK248aS5sZW5ndGgpO2JyZWFrfWlmKDU9PT1pWzFdJiZvK24+PTJ8fDI9PT1pWzFdJiZvK24+PTUpYnJlYWs7aVsxXSYmKG49MSl9d2hpbGUoKytvK3Q8ZS5sZW5ndGgmJm8rbjxpLmxlbmd0aCk7Zm9yKGE9MjthPGkubGVuZ3RoOysrYSktMT09PWlbYV0mJihpW2FdPTApO3N3aXRjaChpWzBdKXtjYXNlIDM4OnIuZmc9dGhpcy5fdXBkYXRlQXR0ckNvbG9yKHIuZmcsaVsxXSxpWzNdLGlbNF0saVs1XSk7YnJlYWs7Y2FzZSA0ODpyLmJnPXRoaXMuX3VwZGF0ZUF0dHJDb2xvcihyLmJnLGlbMV0saVszXSxpWzRdLGlbNV0pO2JyZWFrO2Nhc2UgNTg6ci5leHRlbmRlZD1yLmV4dGVuZGVkLmNsb25lKCksci5leHRlbmRlZC51bmRlcmxpbmVDb2xvcj10aGlzLl91cGRhdGVBdHRyQ29sb3Ioci5leHRlbmRlZC51bmRlcmxpbmVDb2xvcixpWzFdLGlbM10saVs0XSxpWzVdKX1yZXR1cm4gb30sdC5wcm90b3R5cGUuX3Byb2Nlc3NVbmRlcmxpbmU9ZnVuY3Rpb24oZSx0KXt0LmV4dGVuZGVkPXQuZXh0ZW5kZWQuY2xvbmUoKSwoIX5lfHxlPjUpJiYoZT0xKSx0LmV4dGVuZGVkLnVuZGVybGluZVN0eWxlPWUsdC5mZ3w9MjY4NDM1NDU2LDA9PT1lJiYodC5mZyY9LTI2ODQzNTQ1NyksdC51cGRhdGVFeHRlbmRlZCgpfSx0LnByb3RvdHlwZS5jaGFyQXR0cmlidXRlcz1mdW5jdGlvbihlKXtpZigxPT09ZS5sZW5ndGgmJjA9PT1lLnBhcmFtc1swXSlyZXR1cm4gdGhpcy5fY3VyQXR0ckRhdGEuZmc9Zi5ERUZBVUxUX0FUVFJfREFUQS5mZyx0aGlzLl9jdXJBdHRyRGF0YS5iZz1mLkRFRkFVTFRfQVRUUl9EQVRBLmJnLCEwO2Zvcih2YXIgdCxyPWUubGVuZ3RoLGk9dGhpcy5fY3VyQXR0ckRhdGEsbj0wO248cjtuKyspKHQ9ZS5wYXJhbXNbbl0pPj0zMCYmdDw9Mzc/KGkuZmcmPS01MDMzMTkwNCxpLmZnfD0xNjc3NzIxNnx0LTMwKTp0Pj00MCYmdDw9NDc/KGkuYmcmPS01MDMzMTkwNCxpLmJnfD0xNjc3NzIxNnx0LTQwKTp0Pj05MCYmdDw9OTc/KGkuZmcmPS01MDMzMTkwNCxpLmZnfD0xNjc3NzIyNHx0LTkwKTp0Pj0xMDAmJnQ8PTEwNz8oaS5iZyY9LTUwMzMxOTA0LGkuYmd8PTE2Nzc3MjI0fHQtMTAwKTowPT09dD8oaS5mZz1mLkRFRkFVTFRfQVRUUl9EQVRBLmZnLGkuYmc9Zi5ERUZBVUxUX0FUVFJfREFUQS5iZyk6MT09PXQ/aS5mZ3w9MTM0MjE3NzI4OjM9PT10P2kuYmd8PTY3MTA4ODY0OjQ9PT10PyhpLmZnfD0yNjg0MzU0NTYsdGhpcy5fcHJvY2Vzc1VuZGVybGluZShlLmhhc1N1YlBhcmFtcyhuKT9lLmdldFN1YlBhcmFtcyhuKVswXToxLGkpKTo1PT09dD9pLmZnfD01MzY4NzA5MTI6Nz09PXQ/aS5mZ3w9NjcxMDg4NjQ6OD09PXQ/aS5mZ3w9MTA3Mzc0MTgyNDo5PT09dD9pLmZnfD0yMTQ3NDgzNjQ4OjI9PT10P2kuYmd8PTEzNDIxNzcyODoyMT09PXQ/dGhpcy5fcHJvY2Vzc1VuZGVybGluZSgyLGkpOjIyPT09dD8oaS5mZyY9LTEzNDIxNzcyOSxpLmJnJj0tMTM0MjE3NzI5KToyMz09PXQ/aS5iZyY9LTY3MTA4ODY1OjI0PT09dD9pLmZnJj0tMjY4NDM1NDU3OjI1PT09dD9pLmZnJj0tNTM2ODcwOTEzOjI3PT09dD9pLmZnJj0tNjcxMDg4NjU6Mjg9PT10P2kuZmcmPS0xMDczNzQxODI1OjI5PT09dD9pLmZnJj0yMTQ3NDgzNjQ3OjM5PT09dD8oaS5mZyY9LTY3MTA4ODY0LGkuZmd8PTE2Nzc3MjE1JmYuREVGQVVMVF9BVFRSX0RBVEEuZmcpOjQ5PT09dD8oaS5iZyY9LTY3MTA4ODY0LGkuYmd8PTE2Nzc3MjE1JmYuREVGQVVMVF9BVFRSX0RBVEEuYmcpOjM4PT09dHx8NDg9PT10fHw1OD09PXQ/bis9dGhpcy5fZXh0cmFjdENvbG9yKGUsbixpKTo1OT09PXQ/KGkuZXh0ZW5kZWQ9aS5leHRlbmRlZC5jbG9uZSgpLGkuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3I9LTEsaS51cGRhdGVFeHRlbmRlZCgpKToxMDA9PT10PyhpLmZnJj0tNjcxMDg4NjQsaS5mZ3w9MTY3NzcyMTUmZi5ERUZBVUxUX0FUVFJfREFUQS5mZyxpLmJnJj0tNjcxMDg4NjQsaS5iZ3w9MTY3NzcyMTUmZi5ERUZBVUxUX0FUVFJfREFUQS5iZyk6dGhpcy5fbG9nU2VydmljZS5kZWJ1ZygiVW5rbm93biBTR1IgYXR0cmlidXRlOiAlZC4iLHQpO3JldHVybiEwfSx0LnByb3RvdHlwZS5kZXZpY2VTdGF0dXM9ZnVuY3Rpb24oZSl7c3dpdGNoKGUucGFyYW1zWzBdKXtjYXNlIDU6dGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudChzLkMwLkVTQysiWzBuIik7YnJlYWs7Y2FzZSA2OnZhciB0PXRoaXMuX2FjdGl2ZUJ1ZmZlci55KzEscj10aGlzLl9hY3RpdmVCdWZmZXIueCsxO3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIlsiK3QrIjsiK3IrIlIiKX1yZXR1cm4hMH0sdC5wcm90b3R5cGUuZGV2aWNlU3RhdHVzUHJpdmF0ZT1mdW5jdGlvbihlKXtpZig2PT09ZS5wYXJhbXNbMF0pe3ZhciB0PXRoaXMuX2FjdGl2ZUJ1ZmZlci55KzEscj10aGlzLl9hY3RpdmVCdWZmZXIueCsxO3RoaXMuX2NvcmVTZXJ2aWNlLnRyaWdnZXJEYXRhRXZlbnQocy5DMC5FU0MrIls/Iit0KyI7IityKyJSIil9cmV0dXJuITB9LHQucHJvdG90eXBlLnNvZnRSZXNldD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fY29yZVNlcnZpY2UuaXNDdXJzb3JIaWRkZW49ITEsdGhpcy5fb25SZXF1ZXN0U3luY1Njcm9sbEJhci5maXJlKCksdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcD0wLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b209dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzLTEsdGhpcy5fY3VyQXR0ckRhdGE9Zi5ERUZBVUxUX0FUVFJfREFUQS5jbG9uZSgpLHRoaXMuX2NvcmVTZXJ2aWNlLnJlc2V0KCksdGhpcy5fY2hhcnNldFNlcnZpY2UucmVzZXQoKSx0aGlzLl9hY3RpdmVCdWZmZXIuc2F2ZWRYPTAsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkWT10aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ3VyQXR0ckRhdGEuZmc9dGhpcy5fY3VyQXR0ckRhdGEuZmcsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ3VyQXR0ckRhdGEuYmc9dGhpcy5fY3VyQXR0ckRhdGEuYmcsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ2hhcnNldD10aGlzLl9jaGFyc2V0U2VydmljZS5jaGFyc2V0LHRoaXMuX2NvcmVTZXJ2aWNlLmRlY1ByaXZhdGVNb2Rlcy5vcmlnaW49ITEsITB9LHQucHJvdG90eXBlLnNldEN1cnNvclN0eWxlPWZ1bmN0aW9uKGUpe3ZhciB0PWUucGFyYW1zWzBdfHwxO3N3aXRjaCh0KXtjYXNlIDE6Y2FzZSAyOnRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGU9ImJsb2NrIjticmVhaztjYXNlIDM6Y2FzZSA0OnRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuY3Vyc29yU3R5bGU9InVuZGVybGluZSI7YnJlYWs7Y2FzZSA1OmNhc2UgNjp0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmN1cnNvclN0eWxlPSJiYXIifXZhciByPXQlMj09MTtyZXR1cm4gdGhpcy5fb3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5jdXJzb3JCbGluaz1yLCEwfSx0LnByb3RvdHlwZS5zZXRTY3JvbGxSZWdpb249ZnVuY3Rpb24oZSl7dmFyIHQscj1lLnBhcmFtc1swXXx8MTtyZXR1cm4oZS5sZW5ndGg8Mnx8KHQ9ZS5wYXJhbXNbMV0pPnRoaXMuX2J1ZmZlclNlcnZpY2Uucm93c3x8MD09PXQpJiYodD10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MpLHQ+ciYmKHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3A9ci0xLHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxCb3R0b209dC0xLHRoaXMuX3NldEN1cnNvcigwLDApKSwhMH0sdC5wcm90b3R5cGUud2luZG93T3B0aW9ucz1mdW5jdGlvbihlKXtpZighdyhlLnBhcmFtc1swXSx0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLndpbmRvd09wdGlvbnMpKXJldHVybiEwO3ZhciB0PWUubGVuZ3RoPjE/ZS5wYXJhbXNbMV06MDtzd2l0Y2goZS5wYXJhbXNbMF0pe2Nhc2UgMTQ6MiE9PXQmJnRoaXMuX29uUmVxdWVzdFdpbmRvd3NPcHRpb25zUmVwb3J0LmZpcmUoby5HRVRfV0lOX1NJWkVfUElYRUxTKTticmVhaztjYXNlIDE2OnRoaXMuX29uUmVxdWVzdFdpbmRvd3NPcHRpb25zUmVwb3J0LmZpcmUoby5HRVRfQ0VMTF9TSVpFX1BJWEVMUyk7YnJlYWs7Y2FzZSAxODp0aGlzLl9idWZmZXJTZXJ2aWNlJiZ0aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyRGF0YUV2ZW50KHMuQzAuRVNDKyJbODsiK3RoaXMuX2J1ZmZlclNlcnZpY2Uucm93cysiOyIrdGhpcy5fYnVmZmVyU2VydmljZS5jb2xzKyJ0Iik7YnJlYWs7Y2FzZSAyMjowIT09dCYmMiE9PXR8fCh0aGlzLl93aW5kb3dUaXRsZVN0YWNrLnB1c2godGhpcy5fd2luZG93VGl0bGUpLHRoaXMuX3dpbmRvd1RpdGxlU3RhY2subGVuZ3RoPjEwJiZ0aGlzLl93aW5kb3dUaXRsZVN0YWNrLnNoaWZ0KCkpLDAhPT10JiYxIT09dHx8KHRoaXMuX2ljb25OYW1lU3RhY2sucHVzaCh0aGlzLl9pY29uTmFtZSksdGhpcy5faWNvbk5hbWVTdGFjay5sZW5ndGg+MTAmJnRoaXMuX2ljb25OYW1lU3RhY2suc2hpZnQoKSk7YnJlYWs7Y2FzZSAyMzowIT09dCYmMiE9PXR8fHRoaXMuX3dpbmRvd1RpdGxlU3RhY2subGVuZ3RoJiZ0aGlzLnNldFRpdGxlKHRoaXMuX3dpbmRvd1RpdGxlU3RhY2sucG9wKCkpLDAhPT10JiYxIT09dHx8dGhpcy5faWNvbk5hbWVTdGFjay5sZW5ndGgmJnRoaXMuc2V0SWNvbk5hbWUodGhpcy5faWNvbk5hbWVTdGFjay5wb3AoKSl9cmV0dXJuITB9LHQucHJvdG90eXBlLnNhdmVDdXJzb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZFg9dGhpcy5fYWN0aXZlQnVmZmVyLngsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkWT10aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnksdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ3VyQXR0ckRhdGEuZmc9dGhpcy5fY3VyQXR0ckRhdGEuZmcsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ3VyQXR0ckRhdGEuYmc9dGhpcy5fY3VyQXR0ckRhdGEuYmcsdGhpcy5fYWN0aXZlQnVmZmVyLnNhdmVkQ2hhcnNldD10aGlzLl9jaGFyc2V0U2VydmljZS5jaGFyc2V0LCEwfSx0LnByb3RvdHlwZS5yZXN0b3JlQ3Vyc29yPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9hY3RpdmVCdWZmZXIueD10aGlzLl9hY3RpdmVCdWZmZXIuc2F2ZWRYfHwwLHRoaXMuX2FjdGl2ZUJ1ZmZlci55PU1hdGgubWF4KHRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZFktdGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlLDApLHRoaXMuX2N1ckF0dHJEYXRhLmZnPXRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZEN1ckF0dHJEYXRhLmZnLHRoaXMuX2N1ckF0dHJEYXRhLmJnPXRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZEN1ckF0dHJEYXRhLmJnLHRoaXMuX2NoYXJzZXRTZXJ2aWNlLmNoYXJzZXQ9dGhpcy5fc2F2ZWRDaGFyc2V0LHRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZENoYXJzZXQmJih0aGlzLl9jaGFyc2V0U2VydmljZS5jaGFyc2V0PXRoaXMuX2FjdGl2ZUJ1ZmZlci5zYXZlZENoYXJzZXQpLHRoaXMuX3Jlc3RyaWN0Q3Vyc29yKCksITB9LHQucHJvdG90eXBlLnNldFRpdGxlPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl93aW5kb3dUaXRsZT1lLHRoaXMuX29uVGl0bGVDaGFuZ2UuZmlyZShlKSwhMH0sdC5wcm90b3R5cGUuc2V0SWNvbk5hbWU9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2ljb25OYW1lPWUsITB9LHQucHJvdG90eXBlLnNldE9yUmVwb3J0SW5kZXhlZENvbG9yPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1bXSxyPWUuc3BsaXQoIjsiKTtyLmxlbmd0aD4xOyl7dmFyIGk9ci5zaGlmdCgpLG49ci5zaGlmdCgpO2lmKC9eXGQrJC8uZXhlYyhpKSl7dmFyIG89cGFyc2VJbnQoaSk7aWYoMDw9byYmbzwyNTYpaWYoIj8iPT09bil0LnB1c2goe3R5cGU6MCxpbmRleDpvfSk7ZWxzZXt2YXIgcz0oMCxiLnBhcnNlQ29sb3IpKG4pO3MmJnQucHVzaCh7dHlwZToxLGluZGV4Om8sY29sb3I6c30pfX19cmV0dXJuIHQubGVuZ3RoJiZ0aGlzLl9vbkNvbG9yLmZpcmUodCksITB9LHQucHJvdG90eXBlLl9zZXRPclJlcG9ydFNwZWNpYWxDb2xvcj1mdW5jdGlvbihlLHQpe2Zvcih2YXIgcj1lLnNwbGl0KCI7IiksaT0wO2k8ci5sZW5ndGgmJiEodD49dGhpcy5fc3BlY2lhbENvbG9ycy5sZW5ndGgpOysraSwrK3QpaWYoIj8iPT09cltpXSl0aGlzLl9vbkNvbG9yLmZpcmUoW3t0eXBlOjAsaW5kZXg6dGhpcy5fc3BlY2lhbENvbG9yc1t0XX1dKTtlbHNle3ZhciBuPSgwLGIucGFyc2VDb2xvcikocltpXSk7biYmdGhpcy5fb25Db2xvci5maXJlKFt7dHlwZToxLGluZGV4OnRoaXMuX3NwZWNpYWxDb2xvcnNbdF0sY29sb3I6bn1dKX1yZXR1cm4hMH0sdC5wcm90b3R5cGUuc2V0T3JSZXBvcnRGZ0NvbG9yPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9zZXRPclJlcG9ydFNwZWNpYWxDb2xvcihlLDApfSx0LnByb3RvdHlwZS5zZXRPclJlcG9ydEJnQ29sb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX3NldE9yUmVwb3J0U3BlY2lhbENvbG9yKGUsMSl9LHQucHJvdG90eXBlLnNldE9yUmVwb3J0Q3Vyc29yQ29sb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX3NldE9yUmVwb3J0U3BlY2lhbENvbG9yKGUsMil9LHQucHJvdG90eXBlLnJlc3RvcmVJbmRleGVkQ29sb3I9ZnVuY3Rpb24oZSl7aWYoIWUpcmV0dXJuIHRoaXMuX29uQ29sb3IuZmlyZShbe3R5cGU6Mn1dKSwhMDtmb3IodmFyIHQ9W10scj1lLnNwbGl0KCI7IiksaT0wO2k8ci5sZW5ndGg7KytpKWlmKC9eXGQrJC8uZXhlYyhyW2ldKSl7dmFyIG49cGFyc2VJbnQocltpXSk7MDw9biYmbjwyNTYmJnQucHVzaCh7dHlwZToyLGluZGV4Om59KX1yZXR1cm4gdC5sZW5ndGgmJnRoaXMuX29uQ29sb3IuZmlyZSh0KSwhMH0sdC5wcm90b3R5cGUucmVzdG9yZUZnQ29sb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX29uQ29sb3IuZmlyZShbe3R5cGU6MixpbmRleDoyNTZ9XSksITB9LHQucHJvdG90eXBlLnJlc3RvcmVCZ0NvbG9yPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9vbkNvbG9yLmZpcmUoW3t0eXBlOjIsaW5kZXg6MjU3fV0pLCEwfSx0LnByb3RvdHlwZS5yZXN0b3JlQ3Vyc29yQ29sb3I9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX29uQ29sb3IuZmlyZShbe3R5cGU6MixpbmRleDoyNTh9XSksITB9LHQucHJvdG90eXBlLm5leHRMaW5lPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2FjdGl2ZUJ1ZmZlci54PTAsdGhpcy5pbmRleCgpLCEwfSx0LnByb3RvdHlwZS5rZXlwYWRBcHBsaWNhdGlvbk1vZGU9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fbG9nU2VydmljZS5kZWJ1ZygiU2VyaWFsIHBvcnQgcmVxdWVzdGVkIGFwcGxpY2F0aW9uIGtleXBhZC4iKSx0aGlzLl9jb3JlU2VydmljZS5kZWNQcml2YXRlTW9kZXMuYXBwbGljYXRpb25LZXlwYWQ9ITAsdGhpcy5fb25SZXF1ZXN0U3luY1Njcm9sbEJhci5maXJlKCksITB9LHQucHJvdG90eXBlLmtleXBhZE51bWVyaWNNb2RlPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoIlN3aXRjaGluZyBiYWNrIHRvIG5vcm1hbCBrZXlwYWQuIiksdGhpcy5fY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLmFwcGxpY2F0aW9uS2V5cGFkPSExLHRoaXMuX29uUmVxdWVzdFN5bmNTY3JvbGxCYXIuZmlyZSgpLCEwfSx0LnByb3RvdHlwZS5zZWxlY3REZWZhdWx0Q2hhcnNldD1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jaGFyc2V0U2VydmljZS5zZXRnTGV2ZWwoMCksdGhpcy5fY2hhcnNldFNlcnZpY2Uuc2V0Z0NoYXJzZXQoMCxhLkRFRkFVTFRfQ0hBUlNFVCksITB9LHQucHJvdG90eXBlLnNlbGVjdENoYXJzZXQ9ZnVuY3Rpb24oZSl7cmV0dXJuIDIhPT1lLmxlbmd0aD8odGhpcy5zZWxlY3REZWZhdWx0Q2hhcnNldCgpLCEwKTooIi8iPT09ZVswXXx8dGhpcy5fY2hhcnNldFNlcnZpY2Uuc2V0Z0NoYXJzZXQoU1tlWzBdXSxhLkNIQVJTRVRTW2VbMV1dfHxhLkRFRkFVTFRfQ0hBUlNFVCksITApfSx0LnByb3RvdHlwZS5pbmRleD1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9yZXN0cmljdEN1cnNvcigpLHRoaXMuX2FjdGl2ZUJ1ZmZlci55KyssdGhpcy5fYWN0aXZlQnVmZmVyLnk9PT10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsQm90dG9tKzE/KHRoaXMuX2FjdGl2ZUJ1ZmZlci55LS0sdGhpcy5fYnVmZmVyU2VydmljZS5zY3JvbGwodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSk6dGhpcy5fYWN0aXZlQnVmZmVyLnk+PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cyYmKHRoaXMuX2FjdGl2ZUJ1ZmZlci55PXRoaXMuX2J1ZmZlclNlcnZpY2Uucm93cy0xKSx0aGlzLl9yZXN0cmljdEN1cnNvcigpLCEwfSx0LnByb3RvdHlwZS50YWJTZXQ9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYWN0aXZlQnVmZmVyLnRhYnNbdGhpcy5fYWN0aXZlQnVmZmVyLnhdPSEwLCEwfSx0LnByb3RvdHlwZS5yZXZlcnNlSW5kZXg9ZnVuY3Rpb24oKXtpZih0aGlzLl9yZXN0cmljdEN1cnNvcigpLHRoaXMuX2FjdGl2ZUJ1ZmZlci55PT09dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbFRvcCl7dmFyIGU9dGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbS10aGlzLl9hY3RpdmVCdWZmZXIuc2Nyb2xsVG9wO3RoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zaGlmdEVsZW1lbnRzKHRoaXMuX2FjdGl2ZUJ1ZmZlci55YmFzZSt0aGlzLl9hY3RpdmVCdWZmZXIueSxlLDEpLHRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5zZXQodGhpcy5fYWN0aXZlQnVmZmVyLnliYXNlK3RoaXMuX2FjdGl2ZUJ1ZmZlci55LHRoaXMuX2FjdGl2ZUJ1ZmZlci5nZXRCbGFua0xpbmUodGhpcy5fZXJhc2VBdHRyRGF0YSgpKSksdGhpcy5fZGlydHlSb3dTZXJ2aWNlLm1hcmtSYW5nZURpcnR5KHRoaXMuX2FjdGl2ZUJ1ZmZlci5zY3JvbGxUb3AsdGhpcy5fYWN0aXZlQnVmZmVyLnNjcm9sbEJvdHRvbSl9ZWxzZSB0aGlzLl9hY3RpdmVCdWZmZXIueS0tLHRoaXMuX3Jlc3RyaWN0Q3Vyc29yKCk7cmV0dXJuITB9LHQucHJvdG90eXBlLmZ1bGxSZXNldD1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9wYXJzZXIucmVzZXQoKSx0aGlzLl9vblJlcXVlc3RSZXNldC5maXJlKCksITB9LHQucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5fY3VyQXR0ckRhdGE9Zi5ERUZBVUxUX0FUVFJfREFUQS5jbG9uZSgpLHRoaXMuX2VyYXNlQXR0ckRhdGFJbnRlcm5hbD1mLkRFRkFVTFRfQVRUUl9EQVRBLmNsb25lKCl9LHQucHJvdG90eXBlLl9lcmFzZUF0dHJEYXRhPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2VyYXNlQXR0ckRhdGFJbnRlcm5hbC5iZyY9LTY3MTA4ODY0LHRoaXMuX2VyYXNlQXR0ckRhdGFJbnRlcm5hbC5iZ3w9NjcxMDg4NjMmdGhpcy5fY3VyQXR0ckRhdGEuYmcsdGhpcy5fZXJhc2VBdHRyRGF0YUludGVybmFsfSx0LnByb3RvdHlwZS5zZXRnTGV2ZWw9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2NoYXJzZXRTZXJ2aWNlLnNldGdMZXZlbChlKSwhMH0sdC5wcm90b3R5cGUuc2NyZWVuQWxpZ25tZW50UGF0dGVybj1mdW5jdGlvbigpe3ZhciBlPW5ldyBwLkNlbGxEYXRhO2UuY29udGVudD0xPDwyMnwiRSIuY2hhckNvZGVBdCgwKSxlLmZnPXRoaXMuX2N1ckF0dHJEYXRhLmZnLGUuYmc9dGhpcy5fY3VyQXR0ckRhdGEuYmcsdGhpcy5fc2V0Q3Vyc29yKDAsMCk7Zm9yKHZhciB0PTA7dDx0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3M7Kyt0KXt2YXIgcj10aGlzLl9hY3RpdmVCdWZmZXIueWJhc2UrdGhpcy5fYWN0aXZlQnVmZmVyLnkrdCxpPXRoaXMuX2FjdGl2ZUJ1ZmZlci5saW5lcy5nZXQocik7aSYmKGkuZmlsbChlKSxpLmlzV3JhcHBlZD0hMSl9cmV0dXJuIHRoaXMuX2RpcnR5Um93U2VydmljZS5tYXJrQWxsRGlydHkoKSx0aGlzLl9zZXRDdXJzb3IoMCwwKSwhMH0sdH0obC5EaXNwb3NhYmxlKTt0LklucHV0SGFuZGxlcj1FfSw4NDQ6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5nZXREaXNwb3NlQXJyYXlEaXNwb3NhYmxlPXQuZGlzcG9zZUFycmF5PXQuRGlzcG9zYWJsZT12b2lkIDA7dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKCl7dGhpcy5fZGlzcG9zYWJsZXM9W10sdGhpcy5faXNEaXNwb3NlZD0hMX1yZXR1cm4gZS5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3RoaXMuX2lzRGlzcG9zZWQ9ITA7Zm9yKHZhciBlPTAsdD10aGlzLl9kaXNwb3NhYmxlcztlPHQubGVuZ3RoO2UrKyl0W2VdLmRpc3Bvc2UoKTt0aGlzLl9kaXNwb3NhYmxlcy5sZW5ndGg9MH0sZS5wcm90b3R5cGUucmVnaXN0ZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2Rpc3Bvc2FibGVzLnB1c2goZSksZX0sZS5wcm90b3R5cGUudW5yZWdpc3Rlcj1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9kaXNwb3NhYmxlcy5pbmRleE9mKGUpOy0xIT09dCYmdGhpcy5fZGlzcG9zYWJsZXMuc3BsaWNlKHQsMSl9LGV9KCk7ZnVuY3Rpb24gaShlKXtmb3IodmFyIHQ9MCxyPWU7dDxyLmxlbmd0aDt0Kyspclt0XS5kaXNwb3NlKCk7ZS5sZW5ndGg9MH10LkRpc3Bvc2FibGU9cix0LmRpc3Bvc2VBcnJheT1pLHQuZ2V0RGlzcG9zZUFycmF5RGlzcG9zYWJsZT1mdW5jdGlvbihlKXtyZXR1cm57ZGlzcG9zZTpmdW5jdGlvbigpe3JldHVybiBpKGUpfX19fSw2MTE0OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuaXNMaW51eD10LmlzV2luZG93cz10LmlzSXBob25lPXQuaXNJcGFkPXQuaXNNYWM9dC5pc1NhZmFyaT10LmlzRmlyZWZveD12b2lkIDA7dmFyIHI9InVuZGVmaW5lZCI9PXR5cGVvZiBuYXZpZ2F0b3IsaT1yPyJub2RlIjpuYXZpZ2F0b3IudXNlckFnZW50LG49cj8ibm9kZSI6bmF2aWdhdG9yLnBsYXRmb3JtO3QuaXNGaXJlZm94PWkuaW5jbHVkZXMoIkZpcmVmb3giKSx0LmlzU2FmYXJpPS9eKCg/IWNocm9tZXxhbmRyb2lkKS4pKnNhZmFyaS9pLnRlc3QoaSksdC5pc01hYz1bIk1hY2ludG9zaCIsIk1hY0ludGVsIiwiTWFjUFBDIiwiTWFjNjhLIl0uaW5jbHVkZXMobiksdC5pc0lwYWQ9ImlQYWQiPT09bix0LmlzSXBob25lPSJpUGhvbmUiPT09bix0LmlzV2luZG93cz1bIldpbmRvd3MiLCJXaW4xNiIsIldpbjMyIiwiV2luQ0UiXS5pbmNsdWRlcyhuKSx0LmlzTGludXg9bi5pbmRleE9mKCJMaW51eCIpPj0wfSw4MjczOihlLHQpPT57ZnVuY3Rpb24gcihlLHQscixpKXtpZih2b2lkIDA9PT1yJiYocj0wKSx2b2lkIDA9PT1pJiYoaT1lLmxlbmd0aCkscj49ZS5sZW5ndGgpcmV0dXJuIGU7cj0oZS5sZW5ndGgrciklZS5sZW5ndGgsaT1pPj1lLmxlbmd0aD9lLmxlbmd0aDooZS5sZW5ndGgraSklZS5sZW5ndGg7Zm9yKHZhciBuPXI7bjxpOysrbillW25dPXQ7cmV0dXJuIGV9T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuY29uY2F0PXQuZmlsbEZhbGxiYWNrPXQuZmlsbD12b2lkIDAsdC5maWxsPWZ1bmN0aW9uKGUsdCxpLG4pe3JldHVybiBlLmZpbGw/ZS5maWxsKHQsaSxuKTpyKGUsdCxpLG4pfSx0LmZpbGxGYWxsYmFjaz1yLHQuY29uY2F0PWZ1bmN0aW9uKGUsdCl7dmFyIHI9bmV3IGUuY29uc3RydWN0b3IoZS5sZW5ndGgrdC5sZW5ndGgpO3JldHVybiByLnNldChlKSxyLnNldCh0LGUubGVuZ3RoKSxyfX0sOTI4MjooZSx0LHIpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQudXBkYXRlV2luZG93c01vZGVXcmFwcGVkU3RhdGU9dm9pZCAwO3ZhciBpPXIoNjQzKTt0LnVwZGF0ZVdpbmRvd3NNb2RlV3JhcHBlZFN0YXRlPWZ1bmN0aW9uKGUpe3ZhciB0PWUuYnVmZmVyLmxpbmVzLmdldChlLmJ1ZmZlci55YmFzZStlLmJ1ZmZlci55LTEpLHI9bnVsbD09dD92b2lkIDA6dC5nZXQoZS5jb2xzLTEpLG49ZS5idWZmZXIubGluZXMuZ2V0KGUuYnVmZmVyLnliYXNlK2UuYnVmZmVyLnkpO24mJnImJihuLmlzV3JhcHBlZD1yW2kuQ0hBUl9EQVRBX0NPREVfSU5ERVhdIT09aS5OVUxMX0NFTExfQ09ERSYmcltpLkNIQVJfREFUQV9DT0RFX0lOREVYXSE9PWkuV0hJVEVTUEFDRV9DRUxMX0NPREUpfX0sMzczNDooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkV4dGVuZGVkQXR0cnM9dC5BdHRyaWJ1dGVEYXRhPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt0aGlzLmZnPTAsdGhpcy5iZz0wLHRoaXMuZXh0ZW5kZWQ9bmV3IGl9cmV0dXJuIGUudG9Db2xvclJHQj1mdW5jdGlvbihlKXtyZXR1cm5bZT4+PjE2JjI1NSxlPj4+OCYyNTUsMjU1JmVdfSxlLmZyb21Db2xvclJHQj1mdW5jdGlvbihlKXtyZXR1cm4oMjU1JmVbMF0pPDwxNnwoMjU1JmVbMV0pPDw4fDI1NSZlWzJdfSxlLnByb3RvdHlwZS5jbG9uZT1mdW5jdGlvbigpe3ZhciB0PW5ldyBlO3JldHVybiB0LmZnPXRoaXMuZmcsdC5iZz10aGlzLmJnLHQuZXh0ZW5kZWQ9dGhpcy5leHRlbmRlZC5jbG9uZSgpLHR9LGUucHJvdG90eXBlLmlzSW52ZXJzZT1mdW5jdGlvbigpe3JldHVybiA2NzEwODg2NCZ0aGlzLmZnfSxlLnByb3RvdHlwZS5pc0JvbGQ9ZnVuY3Rpb24oKXtyZXR1cm4gMTM0MjE3NzI4JnRoaXMuZmd9LGUucHJvdG90eXBlLmlzVW5kZXJsaW5lPWZ1bmN0aW9uKCl7cmV0dXJuIDI2ODQzNTQ1NiZ0aGlzLmZnfSxlLnByb3RvdHlwZS5pc0JsaW5rPWZ1bmN0aW9uKCl7cmV0dXJuIDUzNjg3MDkxMiZ0aGlzLmZnfSxlLnByb3RvdHlwZS5pc0ludmlzaWJsZT1mdW5jdGlvbigpe3JldHVybiAxMDczNzQxODI0JnRoaXMuZmd9LGUucHJvdG90eXBlLmlzSXRhbGljPWZ1bmN0aW9uKCl7cmV0dXJuIDY3MTA4ODY0JnRoaXMuYmd9LGUucHJvdG90eXBlLmlzRGltPWZ1bmN0aW9uKCl7cmV0dXJuIDEzNDIxNzcyOCZ0aGlzLmJnfSxlLnByb3RvdHlwZS5pc1N0cmlrZXRocm91Z2g9ZnVuY3Rpb24oKXtyZXR1cm4gMjE0NzQ4MzY0OCZ0aGlzLmZnfSxlLnByb3RvdHlwZS5nZXRGZ0NvbG9yTW9kZT1mdW5jdGlvbigpe3JldHVybiA1MDMzMTY0OCZ0aGlzLmZnfSxlLnByb3RvdHlwZS5nZXRCZ0NvbG9yTW9kZT1mdW5jdGlvbigpe3JldHVybiA1MDMzMTY0OCZ0aGlzLmJnfSxlLnByb3RvdHlwZS5pc0ZnUkdCPWZ1bmN0aW9uKCl7cmV0dXJuIDUwMzMxNjQ4PT0oNTAzMzE2NDgmdGhpcy5mZyl9LGUucHJvdG90eXBlLmlzQmdSR0I9ZnVuY3Rpb24oKXtyZXR1cm4gNTAzMzE2NDg9PSg1MDMzMTY0OCZ0aGlzLmJnKX0sZS5wcm90b3R5cGUuaXNGZ1BhbGV0dGU9ZnVuY3Rpb24oKXtyZXR1cm4gMTY3NzcyMTY9PSg1MDMzMTY0OCZ0aGlzLmZnKXx8MzM1NTQ0MzI9PSg1MDMzMTY0OCZ0aGlzLmZnKX0sZS5wcm90b3R5cGUuaXNCZ1BhbGV0dGU9ZnVuY3Rpb24oKXtyZXR1cm4gMTY3NzcyMTY9PSg1MDMzMTY0OCZ0aGlzLmJnKXx8MzM1NTQ0MzI9PSg1MDMzMTY0OCZ0aGlzLmJnKX0sZS5wcm90b3R5cGUuaXNGZ0RlZmF1bHQ9ZnVuY3Rpb24oKXtyZXR1cm4gMD09KDUwMzMxNjQ4JnRoaXMuZmcpfSxlLnByb3RvdHlwZS5pc0JnRGVmYXVsdD1mdW5jdGlvbigpe3JldHVybiAwPT0oNTAzMzE2NDgmdGhpcy5iZyl9LGUucHJvdG90eXBlLmlzQXR0cmlidXRlRGVmYXVsdD1mdW5jdGlvbigpe3JldHVybiAwPT09dGhpcy5mZyYmMD09PXRoaXMuYmd9LGUucHJvdG90eXBlLmdldEZnQ29sb3I9ZnVuY3Rpb24oKXtzd2l0Y2goNTAzMzE2NDgmdGhpcy5mZyl7Y2FzZSAxNjc3NzIxNjpjYXNlIDMzNTU0NDMyOnJldHVybiAyNTUmdGhpcy5mZztjYXNlIDUwMzMxNjQ4OnJldHVybiAxNjc3NzIxNSZ0aGlzLmZnO2RlZmF1bHQ6cmV0dXJuLTF9fSxlLnByb3RvdHlwZS5nZXRCZ0NvbG9yPWZ1bmN0aW9uKCl7c3dpdGNoKDUwMzMxNjQ4JnRoaXMuYmcpe2Nhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpyZXR1cm4gMjU1JnRoaXMuYmc7Y2FzZSA1MDMzMTY0ODpyZXR1cm4gMTY3NzcyMTUmdGhpcy5iZztkZWZhdWx0OnJldHVybi0xfX0sZS5wcm90b3R5cGUuaGFzRXh0ZW5kZWRBdHRycz1mdW5jdGlvbigpe3JldHVybiAyNjg0MzU0NTYmdGhpcy5iZ30sZS5wcm90b3R5cGUudXBkYXRlRXh0ZW5kZWQ9ZnVuY3Rpb24oKXt0aGlzLmV4dGVuZGVkLmlzRW1wdHkoKT90aGlzLmJnJj0tMjY4NDM1NDU3OnRoaXMuYmd8PTI2ODQzNTQ1Nn0sZS5wcm90b3R5cGUuZ2V0VW5kZXJsaW5lQ29sb3I9ZnVuY3Rpb24oKXtpZigyNjg0MzU0NTYmdGhpcy5iZyYmfnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3Ipc3dpdGNoKDUwMzMxNjQ4JnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3Ipe2Nhc2UgMTY3NzcyMTY6Y2FzZSAzMzU1NDQzMjpyZXR1cm4gMjU1JnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3I7Y2FzZSA1MDMzMTY0ODpyZXR1cm4gMTY3NzcyMTUmdGhpcy5leHRlbmRlZC51bmRlcmxpbmVDb2xvcjtkZWZhdWx0OnJldHVybiB0aGlzLmdldEZnQ29sb3IoKX1yZXR1cm4gdGhpcy5nZXRGZ0NvbG9yKCl9LGUucHJvdG90eXBlLmdldFVuZGVybGluZUNvbG9yTW9kZT1mdW5jdGlvbigpe3JldHVybiAyNjg0MzU0NTYmdGhpcy5iZyYmfnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3I/NTAzMzE2NDgmdGhpcy5leHRlbmRlZC51bmRlcmxpbmVDb2xvcjp0aGlzLmdldEZnQ29sb3JNb2RlKCl9LGUucHJvdG90eXBlLmlzVW5kZXJsaW5lQ29sb3JSR0I9ZnVuY3Rpb24oKXtyZXR1cm4gMjY4NDM1NDU2JnRoaXMuYmcmJn50aGlzLmV4dGVuZGVkLnVuZGVybGluZUNvbG9yPzUwMzMxNjQ4PT0oNTAzMzE2NDgmdGhpcy5leHRlbmRlZC51bmRlcmxpbmVDb2xvcik6dGhpcy5pc0ZnUkdCKCl9LGUucHJvdG90eXBlLmlzVW5kZXJsaW5lQ29sb3JQYWxldHRlPWZ1bmN0aW9uKCl7cmV0dXJuIDI2ODQzNTQ1NiZ0aGlzLmJnJiZ+dGhpcy5leHRlbmRlZC51bmRlcmxpbmVDb2xvcj8xNjc3NzIxNj09KDUwMzMxNjQ4JnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3IpfHwzMzU1NDQzMj09KDUwMzMxNjQ4JnRoaXMuZXh0ZW5kZWQudW5kZXJsaW5lQ29sb3IpOnRoaXMuaXNGZ1BhbGV0dGUoKX0sZS5wcm90b3R5cGUuaXNVbmRlcmxpbmVDb2xvckRlZmF1bHQ9ZnVuY3Rpb24oKXtyZXR1cm4gMjY4NDM1NDU2JnRoaXMuYmcmJn50aGlzLmV4dGVuZGVkLnVuZGVybGluZUNvbG9yPzA9PSg1MDMzMTY0OCZ0aGlzLmV4dGVuZGVkLnVuZGVybGluZUNvbG9yKTp0aGlzLmlzRmdEZWZhdWx0KCl9LGUucHJvdG90eXBlLmdldFVuZGVybGluZVN0eWxlPWZ1bmN0aW9uKCl7cmV0dXJuIDI2ODQzNTQ1NiZ0aGlzLmZnPzI2ODQzNTQ1NiZ0aGlzLmJnP3RoaXMuZXh0ZW5kZWQudW5kZXJsaW5lU3R5bGU6MTowfSxlfSgpO3QuQXR0cmlidXRlRGF0YT1yO3ZhciBpPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQpe3ZvaWQgMD09PWUmJihlPTApLHZvaWQgMD09PXQmJih0PS0xKSx0aGlzLnVuZGVybGluZVN0eWxlPWUsdGhpcy51bmRlcmxpbmVDb2xvcj10fXJldHVybiBlLnByb3RvdHlwZS5jbG9uZT1mdW5jdGlvbigpe3JldHVybiBuZXcgZSh0aGlzLnVuZGVybGluZVN0eWxlLHRoaXMudW5kZXJsaW5lQ29sb3IpfSxlLnByb3RvdHlwZS5pc0VtcHR5PWZ1bmN0aW9uKCl7cmV0dXJuIDA9PT10aGlzLnVuZGVybGluZVN0eWxlfSxlfSgpO3QuRXh0ZW5kZWRBdHRycz1pfSw5MDkyOihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5CdWZmZXJTdHJpbmdJdGVyYXRvcj10LkJ1ZmZlcj10Lk1BWF9CVUZGRVJfU0laRT12b2lkIDA7dmFyIGk9cig2MzQ5KSxuPXIoODQzNyksbz1yKDUxMSkscz1yKDY0MyksYT1yKDQ2MzQpLGM9cig0ODYzKSxsPXIoNzExNiksdT1yKDM3MzQpO3QuTUFYX0JVRkZFUl9TSVpFPTQyOTQ5NjcyOTU7dmFyIGg9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyKXt0aGlzLl9oYXNTY3JvbGxiYWNrPWUsdGhpcy5fb3B0aW9uc1NlcnZpY2U9dCx0aGlzLl9idWZmZXJTZXJ2aWNlPXIsdGhpcy55ZGlzcD0wLHRoaXMueWJhc2U9MCx0aGlzLnk9MCx0aGlzLng9MCx0aGlzLnNhdmVkWT0wLHRoaXMuc2F2ZWRYPTAsdGhpcy5zYXZlZEN1ckF0dHJEYXRhPW4uREVGQVVMVF9BVFRSX0RBVEEuY2xvbmUoKSx0aGlzLnNhdmVkQ2hhcnNldD1sLkRFRkFVTFRfQ0hBUlNFVCx0aGlzLm1hcmtlcnM9W10sdGhpcy5fbnVsbENlbGw9by5DZWxsRGF0YS5mcm9tQ2hhckRhdGEoWzAscy5OVUxMX0NFTExfQ0hBUixzLk5VTExfQ0VMTF9XSURUSCxzLk5VTExfQ0VMTF9DT0RFXSksdGhpcy5fd2hpdGVzcGFjZUNlbGw9by5DZWxsRGF0YS5mcm9tQ2hhckRhdGEoWzAscy5XSElURVNQQUNFX0NFTExfQ0hBUixzLldISVRFU1BBQ0VfQ0VMTF9XSURUSCxzLldISVRFU1BBQ0VfQ0VMTF9DT0RFXSksdGhpcy5fY29scz10aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5fcm93cz10aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MsdGhpcy5saW5lcz1uZXcgaS5DaXJjdWxhckxpc3QodGhpcy5fZ2V0Q29ycmVjdEJ1ZmZlckxlbmd0aCh0aGlzLl9yb3dzKSksdGhpcy5zY3JvbGxUb3A9MCx0aGlzLnNjcm9sbEJvdHRvbT10aGlzLl9yb3dzLTEsdGhpcy5zZXR1cFRhYlN0b3BzKCl9cmV0dXJuIGUucHJvdG90eXBlLmdldE51bGxDZWxsPWZ1bmN0aW9uKGUpe3JldHVybiBlPyh0aGlzLl9udWxsQ2VsbC5mZz1lLmZnLHRoaXMuX251bGxDZWxsLmJnPWUuYmcsdGhpcy5fbnVsbENlbGwuZXh0ZW5kZWQ9ZS5leHRlbmRlZCk6KHRoaXMuX251bGxDZWxsLmZnPTAsdGhpcy5fbnVsbENlbGwuYmc9MCx0aGlzLl9udWxsQ2VsbC5leHRlbmRlZD1uZXcgdS5FeHRlbmRlZEF0dHJzKSx0aGlzLl9udWxsQ2VsbH0sZS5wcm90b3R5cGUuZ2V0V2hpdGVzcGFjZUNlbGw9ZnVuY3Rpb24oZSl7cmV0dXJuIGU/KHRoaXMuX3doaXRlc3BhY2VDZWxsLmZnPWUuZmcsdGhpcy5fd2hpdGVzcGFjZUNlbGwuYmc9ZS5iZyx0aGlzLl93aGl0ZXNwYWNlQ2VsbC5leHRlbmRlZD1lLmV4dGVuZGVkKToodGhpcy5fd2hpdGVzcGFjZUNlbGwuZmc9MCx0aGlzLl93aGl0ZXNwYWNlQ2VsbC5iZz0wLHRoaXMuX3doaXRlc3BhY2VDZWxsLmV4dGVuZGVkPW5ldyB1LkV4dGVuZGVkQXR0cnMpLHRoaXMuX3doaXRlc3BhY2VDZWxsfSxlLnByb3RvdHlwZS5nZXRCbGFua0xpbmU9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gbmV3IG4uQnVmZmVyTGluZSh0aGlzLl9idWZmZXJTZXJ2aWNlLmNvbHMsdGhpcy5nZXROdWxsQ2VsbChlKSx0KX0sT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJoYXNTY3JvbGxiYWNrIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2hhc1Njcm9sbGJhY2smJnRoaXMubGluZXMubWF4TGVuZ3RoPnRoaXMuX3Jvd3N9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJpc0N1cnNvckluVmlld3BvcnQiLHtnZXQ6ZnVuY3Rpb24oKXt2YXIgZT10aGlzLnliYXNlK3RoaXMueS10aGlzLnlkaXNwO3JldHVybiBlPj0wJiZlPHRoaXMuX3Jvd3N9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUuX2dldENvcnJlY3RCdWZmZXJMZW5ndGg9ZnVuY3Rpb24oZSl7aWYoIXRoaXMuX2hhc1Njcm9sbGJhY2spcmV0dXJuIGU7dmFyIHI9ZSt0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLnNjcm9sbGJhY2s7cmV0dXJuIHI+dC5NQVhfQlVGRkVSX1NJWkU/dC5NQVhfQlVGRkVSX1NJWkU6cn0sZS5wcm90b3R5cGUuZmlsbFZpZXdwb3J0Um93cz1mdW5jdGlvbihlKXtpZigwPT09dGhpcy5saW5lcy5sZW5ndGgpe3ZvaWQgMD09PWUmJihlPW4uREVGQVVMVF9BVFRSX0RBVEEpO2Zvcih2YXIgdD10aGlzLl9yb3dzO3QtLTspdGhpcy5saW5lcy5wdXNoKHRoaXMuZ2V0QmxhbmtMaW5lKGUpKX19LGUucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7dGhpcy55ZGlzcD0wLHRoaXMueWJhc2U9MCx0aGlzLnk9MCx0aGlzLng9MCx0aGlzLmxpbmVzPW5ldyBpLkNpcmN1bGFyTGlzdCh0aGlzLl9nZXRDb3JyZWN0QnVmZmVyTGVuZ3RoKHRoaXMuX3Jvd3MpKSx0aGlzLnNjcm9sbFRvcD0wLHRoaXMuc2Nyb2xsQm90dG9tPXRoaXMuX3Jvd3MtMSx0aGlzLnNldHVwVGFiU3RvcHMoKX0sZS5wcm90b3R5cGUucmVzaXplPWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5nZXROdWxsQ2VsbChuLkRFRkFVTFRfQVRUUl9EQVRBKSxpPXRoaXMuX2dldENvcnJlY3RCdWZmZXJMZW5ndGgodCk7aWYoaT50aGlzLmxpbmVzLm1heExlbmd0aCYmKHRoaXMubGluZXMubWF4TGVuZ3RoPWkpLHRoaXMubGluZXMubGVuZ3RoPjApe2lmKHRoaXMuX2NvbHM8ZSlmb3IodmFyIG89MDtvPHRoaXMubGluZXMubGVuZ3RoO28rKyl0aGlzLmxpbmVzLmdldChvKS5yZXNpemUoZSxyKTt2YXIgcz0wO2lmKHRoaXMuX3Jvd3M8dClmb3IodmFyIGE9dGhpcy5fcm93czthPHQ7YSsrKXRoaXMubGluZXMubGVuZ3RoPHQrdGhpcy55YmFzZSYmKHRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMud2luZG93c01vZGU/dGhpcy5saW5lcy5wdXNoKG5ldyBuLkJ1ZmZlckxpbmUoZSxyKSk6dGhpcy55YmFzZT4wJiZ0aGlzLmxpbmVzLmxlbmd0aDw9dGhpcy55YmFzZSt0aGlzLnkrcysxPyh0aGlzLnliYXNlLS0scysrLHRoaXMueWRpc3A+MCYmdGhpcy55ZGlzcC0tKTp0aGlzLmxpbmVzLnB1c2gobmV3IG4uQnVmZmVyTGluZShlLHIpKSk7ZWxzZSBmb3IoYT10aGlzLl9yb3dzO2E+dDthLS0pdGhpcy5saW5lcy5sZW5ndGg+dCt0aGlzLnliYXNlJiYodGhpcy5saW5lcy5sZW5ndGg+dGhpcy55YmFzZSt0aGlzLnkrMT90aGlzLmxpbmVzLnBvcCgpOih0aGlzLnliYXNlKyssdGhpcy55ZGlzcCsrKSk7aWYoaTx0aGlzLmxpbmVzLm1heExlbmd0aCl7dmFyIGM9dGhpcy5saW5lcy5sZW5ndGgtaTtjPjAmJih0aGlzLmxpbmVzLnRyaW1TdGFydChjKSx0aGlzLnliYXNlPU1hdGgubWF4KHRoaXMueWJhc2UtYywwKSx0aGlzLnlkaXNwPU1hdGgubWF4KHRoaXMueWRpc3AtYywwKSx0aGlzLnNhdmVkWT1NYXRoLm1heCh0aGlzLnNhdmVkWS1jLDApKSx0aGlzLmxpbmVzLm1heExlbmd0aD1pfXRoaXMueD1NYXRoLm1pbih0aGlzLngsZS0xKSx0aGlzLnk9TWF0aC5taW4odGhpcy55LHQtMSkscyYmKHRoaXMueSs9cyksdGhpcy5zYXZlZFg9TWF0aC5taW4odGhpcy5zYXZlZFgsZS0xKSx0aGlzLnNjcm9sbFRvcD0wfWlmKHRoaXMuc2Nyb2xsQm90dG9tPXQtMSx0aGlzLl9pc1JlZmxvd0VuYWJsZWQmJih0aGlzLl9yZWZsb3coZSx0KSx0aGlzLl9jb2xzPmUpKWZvcihvPTA7bzx0aGlzLmxpbmVzLmxlbmd0aDtvKyspdGhpcy5saW5lcy5nZXQobykucmVzaXplKGUscik7dGhpcy5fY29scz1lLHRoaXMuX3Jvd3M9dH0sT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJfaXNSZWZsb3dFbmFibGVkIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2hhc1Njcm9sbGJhY2smJiF0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLndpbmRvd3NNb2RlfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLl9yZWZsb3c9ZnVuY3Rpb24oZSx0KXt0aGlzLl9jb2xzIT09ZSYmKGU+dGhpcy5fY29scz90aGlzLl9yZWZsb3dMYXJnZXIoZSx0KTp0aGlzLl9yZWZsb3dTbWFsbGVyKGUsdCkpfSxlLnByb3RvdHlwZS5fcmVmbG93TGFyZ2VyPWZ1bmN0aW9uKGUsdCl7dmFyIHI9KDAsYS5yZWZsb3dMYXJnZXJHZXRMaW5lc1RvUmVtb3ZlKSh0aGlzLmxpbmVzLHRoaXMuX2NvbHMsZSx0aGlzLnliYXNlK3RoaXMueSx0aGlzLmdldE51bGxDZWxsKG4uREVGQVVMVF9BVFRSX0RBVEEpKTtpZihyLmxlbmd0aD4wKXt2YXIgaT0oMCxhLnJlZmxvd0xhcmdlckNyZWF0ZU5ld0xheW91dCkodGhpcy5saW5lcyxyKTsoMCxhLnJlZmxvd0xhcmdlckFwcGx5TmV3TGF5b3V0KSh0aGlzLmxpbmVzLGkubGF5b3V0KSx0aGlzLl9yZWZsb3dMYXJnZXJBZGp1c3RWaWV3cG9ydChlLHQsaS5jb3VudFJlbW92ZWQpfX0sZS5wcm90b3R5cGUuX3JlZmxvd0xhcmdlckFkanVzdFZpZXdwb3J0PWZ1bmN0aW9uKGUsdCxyKXtmb3IodmFyIGk9dGhpcy5nZXROdWxsQ2VsbChuLkRFRkFVTFRfQVRUUl9EQVRBKSxvPXI7by0tID4wOykwPT09dGhpcy55YmFzZT8odGhpcy55PjAmJnRoaXMueS0tLHRoaXMubGluZXMubGVuZ3RoPHQmJnRoaXMubGluZXMucHVzaChuZXcgbi5CdWZmZXJMaW5lKGUsaSkpKToodGhpcy55ZGlzcD09PXRoaXMueWJhc2UmJnRoaXMueWRpc3AtLSx0aGlzLnliYXNlLS0pO3RoaXMuc2F2ZWRZPU1hdGgubWF4KHRoaXMuc2F2ZWRZLXIsMCl9LGUucHJvdG90eXBlLl9yZWZsb3dTbWFsbGVyPWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPXRoaXMuZ2V0TnVsbENlbGwobi5ERUZBVUxUX0FUVFJfREFUQSksaT1bXSxvPTAscz10aGlzLmxpbmVzLmxlbmd0aC0xO3M+PTA7cy0tKXt2YXIgYz10aGlzLmxpbmVzLmdldChzKTtpZighKCFjfHwhYy5pc1dyYXBwZWQmJmMuZ2V0VHJpbW1lZExlbmd0aCgpPD1lKSl7Zm9yKHZhciBsPVtjXTtjLmlzV3JhcHBlZCYmcz4wOyljPXRoaXMubGluZXMuZ2V0KC0tcyksbC51bnNoaWZ0KGMpO3ZhciB1PXRoaXMueWJhc2UrdGhpcy55O2lmKCEodT49cyYmdTxzK2wubGVuZ3RoKSl7dmFyIGgsZj1sW2wubGVuZ3RoLTFdLmdldFRyaW1tZWRMZW5ndGgoKSxfPSgwLGEucmVmbG93U21hbGxlckdldE5ld0xpbmVMZW5ndGhzKShsLHRoaXMuX2NvbHMsZSksZD1fLmxlbmd0aC1sLmxlbmd0aDtoPTA9PT10aGlzLnliYXNlJiZ0aGlzLnkhPT10aGlzLmxpbmVzLmxlbmd0aC0xP01hdGgubWF4KDAsdGhpcy55LXRoaXMubGluZXMubWF4TGVuZ3RoK2QpOk1hdGgubWF4KDAsdGhpcy5saW5lcy5sZW5ndGgtdGhpcy5saW5lcy5tYXhMZW5ndGgrZCk7Zm9yKHZhciBwPVtdLHY9MDt2PGQ7disrKXt2YXIgZz10aGlzLmdldEJsYW5rTGluZShuLkRFRkFVTFRfQVRUUl9EQVRBLCEwKTtwLnB1c2goZyl9cC5sZW5ndGg+MCYmKGkucHVzaCh7c3RhcnQ6cytsLmxlbmd0aCtvLG5ld0xpbmVzOnB9KSxvKz1wLmxlbmd0aCksbC5wdXNoLmFwcGx5KGwscCk7dmFyIHk9Xy5sZW5ndGgtMSxtPV9beV07MD09PW0mJihtPV9bLS15XSk7Zm9yKHZhciBiPWwubGVuZ3RoLWQtMSxTPWY7Yj49MDspe3ZhciBDPU1hdGgubWluKFMsbSk7aWYobFt5XS5jb3B5Q2VsbHNGcm9tKGxbYl0sUy1DLG0tQyxDLCEwKSwwPT0obS09QykmJihtPV9bLS15XSksMD09KFMtPUMpKXtiLS07dmFyIHc9TWF0aC5tYXgoYiwwKTtTPSgwLGEuZ2V0V3JhcHBlZExpbmVUcmltbWVkTGVuZ3RoKShsLHcsdGhpcy5fY29scyl9fWZvcih2PTA7djxsLmxlbmd0aDt2KyspX1t2XTxlJiZsW3ZdLnNldENlbGwoX1t2XSxyKTtmb3IodmFyIEw9ZC1oO0wtLSA+MDspMD09PXRoaXMueWJhc2U/dGhpcy55PHQtMT8odGhpcy55KyssdGhpcy5saW5lcy5wb3AoKSk6KHRoaXMueWJhc2UrKyx0aGlzLnlkaXNwKyspOnRoaXMueWJhc2U8TWF0aC5taW4odGhpcy5saW5lcy5tYXhMZW5ndGgsdGhpcy5saW5lcy5sZW5ndGgrbyktdCYmKHRoaXMueWJhc2U9PT10aGlzLnlkaXNwJiZ0aGlzLnlkaXNwKyssdGhpcy55YmFzZSsrKTt0aGlzLnNhdmVkWT1NYXRoLm1pbih0aGlzLnNhdmVkWStkLHRoaXMueWJhc2UrdC0xKX19fWlmKGkubGVuZ3RoPjApe3ZhciBFPVtdLHg9W107Zm9yKHY9MDt2PHRoaXMubGluZXMubGVuZ3RoO3YrKyl4LnB1c2godGhpcy5saW5lcy5nZXQodikpO3ZhciBBPXRoaXMubGluZXMubGVuZ3RoLGs9QS0xLE09MCxSPWlbTV07dGhpcy5saW5lcy5sZW5ndGg9TWF0aC5taW4odGhpcy5saW5lcy5tYXhMZW5ndGgsdGhpcy5saW5lcy5sZW5ndGgrbyk7dmFyIFQ9MDtmb3Iodj1NYXRoLm1pbih0aGlzLmxpbmVzLm1heExlbmd0aC0xLEErby0xKTt2Pj0wO3YtLSlpZihSJiZSLnN0YXJ0PmsrVCl7Zm9yKHZhciBPPVIubmV3TGluZXMubGVuZ3RoLTE7Tz49MDtPLS0pdGhpcy5saW5lcy5zZXQodi0tLFIubmV3TGluZXNbT10pO3YrKyxFLnB1c2goe2luZGV4OmsrMSxhbW91bnQ6Ui5uZXdMaW5lcy5sZW5ndGh9KSxUKz1SLm5ld0xpbmVzLmxlbmd0aCxSPWlbKytNXX1lbHNlIHRoaXMubGluZXMuc2V0KHYseFtrLS1dKTt2YXIgQj0wO2Zvcih2PUUubGVuZ3RoLTE7dj49MDt2LS0pRVt2XS5pbmRleCs9Qix0aGlzLmxpbmVzLm9uSW5zZXJ0RW1pdHRlci5maXJlKEVbdl0pLEIrPUVbdl0uYW1vdW50O3ZhciBEPU1hdGgubWF4KDAsQStvLXRoaXMubGluZXMubWF4TGVuZ3RoKTtEPjAmJnRoaXMubGluZXMub25UcmltRW1pdHRlci5maXJlKEQpfX0sZS5wcm90b3R5cGUuc3RyaW5nSW5kZXhUb0J1ZmZlckluZGV4PWZ1bmN0aW9uKGUsdCxyKXtmb3Iodm9pZCAwPT09ciYmKHI9ITEpO3Q7KXt2YXIgaT10aGlzLmxpbmVzLmdldChlKTtpZighaSlyZXR1cm5bLTEsLTFdO2Zvcih2YXIgbj1yP2kuZ2V0VHJpbW1lZExlbmd0aCgpOmkubGVuZ3RoLG89MDtvPG47KytvKWlmKGkuZ2V0KG8pW3MuQ0hBUl9EQVRBX1dJRFRIX0lOREVYXSYmKHQtPWkuZ2V0KG8pW3MuQ0hBUl9EQVRBX0NIQVJfSU5ERVhdLmxlbmd0aHx8MSksdDwwKXJldHVybltlLG9dO2UrK31yZXR1cm5bZSwwXX0sZS5wcm90b3R5cGUudHJhbnNsYXRlQnVmZmVyTGluZVRvU3RyaW5nPWZ1bmN0aW9uKGUsdCxyLGkpe3ZvaWQgMD09PXImJihyPTApO3ZhciBuPXRoaXMubGluZXMuZ2V0KGUpO3JldHVybiBuP24udHJhbnNsYXRlVG9TdHJpbmcodCxyLGkpOiIifSxlLnByb3RvdHlwZS5nZXRXcmFwcGVkUmFuZ2VGb3JMaW5lPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1lLHI9ZTt0PjAmJnRoaXMubGluZXMuZ2V0KHQpLmlzV3JhcHBlZDspdC0tO2Zvcig7cisxPHRoaXMubGluZXMubGVuZ3RoJiZ0aGlzLmxpbmVzLmdldChyKzEpLmlzV3JhcHBlZDspcisrO3JldHVybntmaXJzdDp0LGxhc3Q6cn19LGUucHJvdG90eXBlLnNldHVwVGFiU3RvcHM9ZnVuY3Rpb24oZSl7Zm9yKG51bGwhPWU/dGhpcy50YWJzW2VdfHwoZT10aGlzLnByZXZTdG9wKGUpKToodGhpcy50YWJzPXt9LGU9MCk7ZTx0aGlzLl9jb2xzO2UrPXRoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMudGFiU3RvcFdpZHRoKXRoaXMudGFic1tlXT0hMH0sZS5wcm90b3R5cGUucHJldlN0b3A9ZnVuY3Rpb24oZSl7Zm9yKG51bGw9PWUmJihlPXRoaXMueCk7IXRoaXMudGFic1stLWVdJiZlPjA7KTtyZXR1cm4gZT49dGhpcy5fY29scz90aGlzLl9jb2xzLTE6ZTwwPzA6ZX0sZS5wcm90b3R5cGUubmV4dFN0b3A9ZnVuY3Rpb24oZSl7Zm9yKG51bGw9PWUmJihlPXRoaXMueCk7IXRoaXMudGFic1srK2VdJiZlPHRoaXMuX2NvbHM7KTtyZXR1cm4gZT49dGhpcy5fY29scz90aGlzLl9jb2xzLTE6ZTwwPzA6ZX0sZS5wcm90b3R5cGUuYWRkTWFya2VyPWZ1bmN0aW9uKGUpe3ZhciB0PXRoaXMscj1uZXcgYy5NYXJrZXIoZSk7cmV0dXJuIHRoaXMubWFya2Vycy5wdXNoKHIpLHIucmVnaXN0ZXIodGhpcy5saW5lcy5vblRyaW0oKGZ1bmN0aW9uKGUpe3IubGluZS09ZSxyLmxpbmU8MCYmci5kaXNwb3NlKCl9KSkpLHIucmVnaXN0ZXIodGhpcy5saW5lcy5vbkluc2VydCgoZnVuY3Rpb24oZSl7ci5saW5lPj1lLmluZGV4JiYoci5saW5lKz1lLmFtb3VudCl9KSkpLHIucmVnaXN0ZXIodGhpcy5saW5lcy5vbkRlbGV0ZSgoZnVuY3Rpb24oZSl7ci5saW5lPj1lLmluZGV4JiZyLmxpbmU8ZS5pbmRleCtlLmFtb3VudCYmci5kaXNwb3NlKCksci5saW5lPmUuaW5kZXgmJihyLmxpbmUtPWUuYW1vdW50KX0pKSksci5yZWdpc3RlcihyLm9uRGlzcG9zZSgoZnVuY3Rpb24oKXtyZXR1cm4gdC5fcmVtb3ZlTWFya2VyKHIpfSkpKSxyfSxlLnByb3RvdHlwZS5fcmVtb3ZlTWFya2VyPWZ1bmN0aW9uKGUpe3RoaXMubWFya2Vycy5zcGxpY2UodGhpcy5tYXJrZXJzLmluZGV4T2YoZSksMSl9LGUucHJvdG90eXBlLml0ZXJhdG9yPWZ1bmN0aW9uKGUsdCxyLGksbil7cmV0dXJuIG5ldyBmKHRoaXMsZSx0LHIsaSxuKX0sZX0oKTt0LkJ1ZmZlcj1oO3ZhciBmPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQscixpLG4sbyl7dm9pZCAwPT09ciYmKHI9MCksdm9pZCAwPT09aSYmKGk9ZS5saW5lcy5sZW5ndGgpLHZvaWQgMD09PW4mJihuPTApLHZvaWQgMD09PW8mJihvPTApLHRoaXMuX2J1ZmZlcj1lLHRoaXMuX3RyaW1SaWdodD10LHRoaXMuX3N0YXJ0SW5kZXg9cix0aGlzLl9lbmRJbmRleD1pLHRoaXMuX3N0YXJ0T3ZlcnNjYW49bix0aGlzLl9lbmRPdmVyc2Nhbj1vLHRoaXMuX3N0YXJ0SW5kZXg8MCYmKHRoaXMuX3N0YXJ0SW5kZXg9MCksdGhpcy5fZW5kSW5kZXg+dGhpcy5fYnVmZmVyLmxpbmVzLmxlbmd0aCYmKHRoaXMuX2VuZEluZGV4PXRoaXMuX2J1ZmZlci5saW5lcy5sZW5ndGgpLHRoaXMuX2N1cnJlbnQ9dGhpcy5fc3RhcnRJbmRleH1yZXR1cm4gZS5wcm90b3R5cGUuaGFzTmV4dD1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jdXJyZW50PHRoaXMuX2VuZEluZGV4fSxlLnByb3RvdHlwZS5uZXh0PWZ1bmN0aW9uKCl7dmFyIGU9dGhpcy5fYnVmZmVyLmdldFdyYXBwZWRSYW5nZUZvckxpbmUodGhpcy5fY3VycmVudCk7ZS5maXJzdDx0aGlzLl9zdGFydEluZGV4LXRoaXMuX3N0YXJ0T3ZlcnNjYW4mJihlLmZpcnN0PXRoaXMuX3N0YXJ0SW5kZXgtdGhpcy5fc3RhcnRPdmVyc2NhbiksZS5sYXN0PnRoaXMuX2VuZEluZGV4K3RoaXMuX2VuZE92ZXJzY2FuJiYoZS5sYXN0PXRoaXMuX2VuZEluZGV4K3RoaXMuX2VuZE92ZXJzY2FuKSxlLmZpcnN0PU1hdGgubWF4KGUuZmlyc3QsMCksZS5sYXN0PU1hdGgubWluKGUubGFzdCx0aGlzLl9idWZmZXIubGluZXMubGVuZ3RoKTtmb3IodmFyIHQ9IiIscj1lLmZpcnN0O3I8PWUubGFzdDsrK3IpdCs9dGhpcy5fYnVmZmVyLnRyYW5zbGF0ZUJ1ZmZlckxpbmVUb1N0cmluZyhyLHRoaXMuX3RyaW1SaWdodCk7cmV0dXJuIHRoaXMuX2N1cnJlbnQ9ZS5sYXN0KzEse3JhbmdlOmUsY29udGVudDp0fX0sZX0oKTt0LkJ1ZmZlclN0cmluZ0l0ZXJhdG9yPWZ9LDg0Mzc6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJ1ZmZlckxpbmU9dC5ERUZBVUxUX0FUVFJfREFUQT12b2lkIDA7dmFyIGk9cig0ODIpLG49cig2NDMpLG89cig1MTEpLHM9cigzNzM0KTt0LkRFRkFVTFRfQVRUUl9EQVRBPU9iamVjdC5mcmVlemUobmV3IHMuQXR0cmlidXRlRGF0YSk7dmFyIGE9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUsdCxyKXt2b2lkIDA9PT1yJiYocj0hMSksdGhpcy5pc1dyYXBwZWQ9cix0aGlzLl9jb21iaW5lZD17fSx0aGlzLl9leHRlbmRlZEF0dHJzPXt9LHRoaXMuX2RhdGE9bmV3IFVpbnQzMkFycmF5KDMqZSk7Zm9yKHZhciBpPXR8fG8uQ2VsbERhdGEuZnJvbUNoYXJEYXRhKFswLG4uTlVMTF9DRUxMX0NIQVIsbi5OVUxMX0NFTExfV0lEVEgsbi5OVUxMX0NFTExfQ09ERV0pLHM9MDtzPGU7KytzKXRoaXMuc2V0Q2VsbChzLGkpO3RoaXMubGVuZ3RoPWV9cmV0dXJuIGUucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9kYXRhWzMqZSswXSxyPTIwOTcxNTEmdDtyZXR1cm5bdGhpcy5fZGF0YVszKmUrMV0sMjA5NzE1MiZ0P3RoaXMuX2NvbWJpbmVkW2VdOnI/KDAsaS5zdHJpbmdGcm9tQ29kZVBvaW50KShyKToiIix0Pj4yMiwyMDk3MTUyJnQ/dGhpcy5fY29tYmluZWRbZV0uY2hhckNvZGVBdCh0aGlzLl9jb21iaW5lZFtlXS5sZW5ndGgtMSk6cl19LGUucHJvdG90eXBlLnNldD1mdW5jdGlvbihlLHQpe3RoaXMuX2RhdGFbMyplKzFdPXRbbi5DSEFSX0RBVEFfQVRUUl9JTkRFWF0sdFtuLkNIQVJfREFUQV9DSEFSX0lOREVYXS5sZW5ndGg+MT8odGhpcy5fY29tYmluZWRbZV09dFsxXSx0aGlzLl9kYXRhWzMqZSswXT0yMDk3MTUyfGV8dFtuLkNIQVJfREFUQV9XSURUSF9JTkRFWF08PDIyKTp0aGlzLl9kYXRhWzMqZSswXT10W24uQ0hBUl9EQVRBX0NIQVJfSU5ERVhdLmNoYXJDb2RlQXQoMCl8dFtuLkNIQVJfREFUQV9XSURUSF9JTkRFWF08PDIyfSxlLnByb3RvdHlwZS5nZXRXaWR0aD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fZGF0YVszKmUrMF0+PjIyfSxlLnByb3RvdHlwZS5oYXNXaWR0aD1mdW5jdGlvbihlKXtyZXR1cm4gMTI1ODI5MTImdGhpcy5fZGF0YVszKmUrMF19LGUucHJvdG90eXBlLmdldEZnPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9kYXRhWzMqZSsxXX0sZS5wcm90b3R5cGUuZ2V0Qmc9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMuX2RhdGFbMyplKzJdfSxlLnByb3RvdHlwZS5oYXNDb250ZW50PWZ1bmN0aW9uKGUpe3JldHVybiA0MTk0MzAzJnRoaXMuX2RhdGFbMyplKzBdfSxlLnByb3RvdHlwZS5nZXRDb2RlUG9pbnQ9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fZGF0YVszKmUrMF07cmV0dXJuIDIwOTcxNTImdD90aGlzLl9jb21iaW5lZFtlXS5jaGFyQ29kZUF0KHRoaXMuX2NvbWJpbmVkW2VdLmxlbmd0aC0xKToyMDk3MTUxJnR9LGUucHJvdG90eXBlLmlzQ29tYmluZWQ9ZnVuY3Rpb24oZSl7cmV0dXJuIDIwOTcxNTImdGhpcy5fZGF0YVszKmUrMF19LGUucHJvdG90eXBlLmdldFN0cmluZz1mdW5jdGlvbihlKXt2YXIgdD10aGlzLl9kYXRhWzMqZSswXTtyZXR1cm4gMjA5NzE1MiZ0P3RoaXMuX2NvbWJpbmVkW2VdOjIwOTcxNTEmdD8oMCxpLnN0cmluZ0Zyb21Db2RlUG9pbnQpKDIwOTcxNTEmdCk6IiJ9LGUucHJvdG90eXBlLmxvYWRDZWxsPWZ1bmN0aW9uKGUsdCl7dmFyIHI9MyplO3JldHVybiB0LmNvbnRlbnQ9dGhpcy5fZGF0YVtyKzBdLHQuZmc9dGhpcy5fZGF0YVtyKzFdLHQuYmc9dGhpcy5fZGF0YVtyKzJdLDIwOTcxNTImdC5jb250ZW50JiYodC5jb21iaW5lZERhdGE9dGhpcy5fY29tYmluZWRbZV0pLDI2ODQzNTQ1NiZ0LmJnJiYodC5leHRlbmRlZD10aGlzLl9leHRlbmRlZEF0dHJzW2VdKSx0fSxlLnByb3RvdHlwZS5zZXRDZWxsPWZ1bmN0aW9uKGUsdCl7MjA5NzE1MiZ0LmNvbnRlbnQmJih0aGlzLl9jb21iaW5lZFtlXT10LmNvbWJpbmVkRGF0YSksMjY4NDM1NDU2JnQuYmcmJih0aGlzLl9leHRlbmRlZEF0dHJzW2VdPXQuZXh0ZW5kZWQpLHRoaXMuX2RhdGFbMyplKzBdPXQuY29udGVudCx0aGlzLl9kYXRhWzMqZSsxXT10LmZnLHRoaXMuX2RhdGFbMyplKzJdPXQuYmd9LGUucHJvdG90eXBlLnNldENlbGxGcm9tQ29kZVBvaW50PWZ1bmN0aW9uKGUsdCxyLGksbixvKXsyNjg0MzU0NTYmbiYmKHRoaXMuX2V4dGVuZGVkQXR0cnNbZV09byksdGhpcy5fZGF0YVszKmUrMF09dHxyPDwyMix0aGlzLl9kYXRhWzMqZSsxXT1pLHRoaXMuX2RhdGFbMyplKzJdPW59LGUucHJvdG90eXBlLmFkZENvZGVwb2ludFRvQ2VsbD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX2RhdGFbMyplKzBdOzIwOTcxNTImcj90aGlzLl9jb21iaW5lZFtlXSs9KDAsaS5zdHJpbmdGcm9tQ29kZVBvaW50KSh0KTooMjA5NzE1MSZyPyh0aGlzLl9jb21iaW5lZFtlXT0oMCxpLnN0cmluZ0Zyb21Db2RlUG9pbnQpKDIwOTcxNTEmcikrKDAsaS5zdHJpbmdGcm9tQ29kZVBvaW50KSh0KSxyJj0tMjA5NzE1MixyfD0yMDk3MTUyKTpyPXR8MTw8MjIsdGhpcy5fZGF0YVszKmUrMF09cil9LGUucHJvdG90eXBlLmluc2VydENlbGxzPWZ1bmN0aW9uKGUsdCxyLGkpe2lmKChlJT10aGlzLmxlbmd0aCkmJjI9PT10aGlzLmdldFdpZHRoKGUtMSkmJnRoaXMuc2V0Q2VsbEZyb21Db2RlUG9pbnQoZS0xLDAsMSwobnVsbD09aT92b2lkIDA6aS5mZyl8fDAsKG51bGw9PWk/dm9pZCAwOmkuYmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmV4dGVuZGVkKXx8bmV3IHMuRXh0ZW5kZWRBdHRycyksdDx0aGlzLmxlbmd0aC1lKXtmb3IodmFyIG49bmV3IG8uQ2VsbERhdGEsYT10aGlzLmxlbmd0aC1lLXQtMTthPj0wOy0tYSl0aGlzLnNldENlbGwoZSt0K2EsdGhpcy5sb2FkQ2VsbChlK2EsbikpO2ZvcihhPTA7YTx0OysrYSl0aGlzLnNldENlbGwoZSthLHIpfWVsc2UgZm9yKGE9ZTthPHRoaXMubGVuZ3RoOysrYSl0aGlzLnNldENlbGwoYSxyKTsyPT09dGhpcy5nZXRXaWR0aCh0aGlzLmxlbmd0aC0xKSYmdGhpcy5zZXRDZWxsRnJvbUNvZGVQb2ludCh0aGlzLmxlbmd0aC0xLDAsMSwobnVsbD09aT92b2lkIDA6aS5mZyl8fDAsKG51bGw9PWk/dm9pZCAwOmkuYmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmV4dGVuZGVkKXx8bmV3IHMuRXh0ZW5kZWRBdHRycyl9LGUucHJvdG90eXBlLmRlbGV0ZUNlbGxzPWZ1bmN0aW9uKGUsdCxyLGkpe2lmKGUlPXRoaXMubGVuZ3RoLHQ8dGhpcy5sZW5ndGgtZSl7Zm9yKHZhciBuPW5ldyBvLkNlbGxEYXRhLGE9MDthPHRoaXMubGVuZ3RoLWUtdDsrK2EpdGhpcy5zZXRDZWxsKGUrYSx0aGlzLmxvYWRDZWxsKGUrdCthLG4pKTtmb3IoYT10aGlzLmxlbmd0aC10O2E8dGhpcy5sZW5ndGg7KythKXRoaXMuc2V0Q2VsbChhLHIpfWVsc2UgZm9yKGE9ZTthPHRoaXMubGVuZ3RoOysrYSl0aGlzLnNldENlbGwoYSxyKTtlJiYyPT09dGhpcy5nZXRXaWR0aChlLTEpJiZ0aGlzLnNldENlbGxGcm9tQ29kZVBvaW50KGUtMSwwLDEsKG51bGw9PWk/dm9pZCAwOmkuZmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmJnKXx8MCwobnVsbD09aT92b2lkIDA6aS5leHRlbmRlZCl8fG5ldyBzLkV4dGVuZGVkQXR0cnMpLDAhPT10aGlzLmdldFdpZHRoKGUpfHx0aGlzLmhhc0NvbnRlbnQoZSl8fHRoaXMuc2V0Q2VsbEZyb21Db2RlUG9pbnQoZSwwLDEsKG51bGw9PWk/dm9pZCAwOmkuZmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmJnKXx8MCwobnVsbD09aT92b2lkIDA6aS5leHRlbmRlZCl8fG5ldyBzLkV4dGVuZGVkQXR0cnMpfSxlLnByb3RvdHlwZS5yZXBsYWNlQ2VsbHM9ZnVuY3Rpb24oZSx0LHIsaSl7Zm9yKGUmJjI9PT10aGlzLmdldFdpZHRoKGUtMSkmJnRoaXMuc2V0Q2VsbEZyb21Db2RlUG9pbnQoZS0xLDAsMSwobnVsbD09aT92b2lkIDA6aS5mZyl8fDAsKG51bGw9PWk/dm9pZCAwOmkuYmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmV4dGVuZGVkKXx8bmV3IHMuRXh0ZW5kZWRBdHRycyksdDx0aGlzLmxlbmd0aCYmMj09PXRoaXMuZ2V0V2lkdGgodC0xKSYmdGhpcy5zZXRDZWxsRnJvbUNvZGVQb2ludCh0LDAsMSwobnVsbD09aT92b2lkIDA6aS5mZyl8fDAsKG51bGw9PWk/dm9pZCAwOmkuYmcpfHwwLChudWxsPT1pP3ZvaWQgMDppLmV4dGVuZGVkKXx8bmV3IHMuRXh0ZW5kZWRBdHRycyk7ZTx0JiZlPHRoaXMubGVuZ3RoOyl0aGlzLnNldENlbGwoZSsrLHIpfSxlLnByb3RvdHlwZS5yZXNpemU9ZnVuY3Rpb24oZSx0KXtpZihlIT09dGhpcy5sZW5ndGgpe2lmKGU+dGhpcy5sZW5ndGgpe3ZhciByPW5ldyBVaW50MzJBcnJheSgzKmUpO3RoaXMubGVuZ3RoJiYoMyplPHRoaXMuX2RhdGEubGVuZ3RoP3Iuc2V0KHRoaXMuX2RhdGEuc3ViYXJyYXkoMCwzKmUpKTpyLnNldCh0aGlzLl9kYXRhKSksdGhpcy5fZGF0YT1yO2Zvcih2YXIgaT10aGlzLmxlbmd0aDtpPGU7KytpKXRoaXMuc2V0Q2VsbChpLHQpfWVsc2UgaWYoZSl7KHI9bmV3IFVpbnQzMkFycmF5KDMqZSkpLnNldCh0aGlzLl9kYXRhLnN1YmFycmF5KDAsMyplKSksdGhpcy5fZGF0YT1yO3ZhciBuPU9iamVjdC5rZXlzKHRoaXMuX2NvbWJpbmVkKTtmb3IoaT0wO2k8bi5sZW5ndGg7aSsrKXt2YXIgbz1wYXJzZUludChuW2ldLDEwKTtvPj1lJiZkZWxldGUgdGhpcy5fY29tYmluZWRbb119fWVsc2UgdGhpcy5fZGF0YT1uZXcgVWludDMyQXJyYXkoMCksdGhpcy5fY29tYmluZWQ9e307dGhpcy5sZW5ndGg9ZX19LGUucHJvdG90eXBlLmZpbGw9ZnVuY3Rpb24oZSl7dGhpcy5fY29tYmluZWQ9e30sdGhpcy5fZXh0ZW5kZWRBdHRycz17fTtmb3IodmFyIHQ9MDt0PHRoaXMubGVuZ3RoOysrdCl0aGlzLnNldENlbGwodCxlKX0sZS5wcm90b3R5cGUuY29weUZyb209ZnVuY3Rpb24oZSl7Zm9yKHZhciB0IGluIHRoaXMubGVuZ3RoIT09ZS5sZW5ndGg/dGhpcy5fZGF0YT1uZXcgVWludDMyQXJyYXkoZS5fZGF0YSk6dGhpcy5fZGF0YS5zZXQoZS5fZGF0YSksdGhpcy5sZW5ndGg9ZS5sZW5ndGgsdGhpcy5fY29tYmluZWQ9e30sZS5fY29tYmluZWQpdGhpcy5fY29tYmluZWRbdF09ZS5fY29tYmluZWRbdF07Zm9yKHZhciB0IGluIHRoaXMuX2V4dGVuZGVkQXR0cnM9e30sZS5fZXh0ZW5kZWRBdHRycyl0aGlzLl9leHRlbmRlZEF0dHJzW3RdPWUuX2V4dGVuZGVkQXR0cnNbdF07dGhpcy5pc1dyYXBwZWQ9ZS5pc1dyYXBwZWR9LGUucHJvdG90eXBlLmNsb25lPWZ1bmN0aW9uKCl7dmFyIHQ9bmV3IGUoMCk7Zm9yKHZhciByIGluIHQuX2RhdGE9bmV3IFVpbnQzMkFycmF5KHRoaXMuX2RhdGEpLHQubGVuZ3RoPXRoaXMubGVuZ3RoLHRoaXMuX2NvbWJpbmVkKXQuX2NvbWJpbmVkW3JdPXRoaXMuX2NvbWJpbmVkW3JdO2Zvcih2YXIgciBpbiB0aGlzLl9leHRlbmRlZEF0dHJzKXQuX2V4dGVuZGVkQXR0cnNbcl09dGhpcy5fZXh0ZW5kZWRBdHRyc1tyXTtyZXR1cm4gdC5pc1dyYXBwZWQ9dGhpcy5pc1dyYXBwZWQsdH0sZS5wcm90b3R5cGUuZ2V0VHJpbW1lZExlbmd0aD1mdW5jdGlvbigpe2Zvcih2YXIgZT10aGlzLmxlbmd0aC0xO2U+PTA7LS1lKWlmKDQxOTQzMDMmdGhpcy5fZGF0YVszKmUrMF0pcmV0dXJuIGUrKHRoaXMuX2RhdGFbMyplKzBdPj4yMik7cmV0dXJuIDB9LGUucHJvdG90eXBlLmNvcHlDZWxsc0Zyb209ZnVuY3Rpb24oZSx0LHIsaSxuKXt2YXIgbz1lLl9kYXRhO2lmKG4pZm9yKHZhciBzPWktMTtzPj0wO3MtLSlmb3IodmFyIGE9MDthPDM7YSsrKXRoaXMuX2RhdGFbMyoocitzKSthXT1vWzMqKHQrcykrYV07ZWxzZSBmb3Iocz0wO3M8aTtzKyspZm9yKGE9MDthPDM7YSsrKXRoaXMuX2RhdGFbMyoocitzKSthXT1vWzMqKHQrcykrYV07dmFyIGM9T2JqZWN0LmtleXMoZS5fY29tYmluZWQpO2ZvcihhPTA7YTxjLmxlbmd0aDthKyspe3ZhciBsPXBhcnNlSW50KGNbYV0sMTApO2w+PXQmJih0aGlzLl9jb21iaW5lZFtsLXQrcl09ZS5fY29tYmluZWRbbF0pfX0sZS5wcm90b3R5cGUudHJhbnNsYXRlVG9TdHJpbmc9ZnVuY3Rpb24oZSx0LHIpe3ZvaWQgMD09PWUmJihlPSExKSx2b2lkIDA9PT10JiYodD0wKSx2b2lkIDA9PT1yJiYocj10aGlzLmxlbmd0aCksZSYmKHI9TWF0aC5taW4ocix0aGlzLmdldFRyaW1tZWRMZW5ndGgoKSkpO2Zvcih2YXIgbz0iIjt0PHI7KXt2YXIgcz10aGlzLl9kYXRhWzMqdCswXSxhPTIwOTcxNTEmcztvKz0yMDk3MTUyJnM/dGhpcy5fY29tYmluZWRbdF06YT8oMCxpLnN0cmluZ0Zyb21Db2RlUG9pbnQpKGEpOm4uV0hJVEVTUEFDRV9DRUxMX0NIQVIsdCs9cz4+MjJ8fDF9cmV0dXJuIG99LGV9KCk7dC5CdWZmZXJMaW5lPWF9LDQ4NDE6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5nZXRSYW5nZUxlbmd0aD12b2lkIDAsdC5nZXRSYW5nZUxlbmd0aD1mdW5jdGlvbihlLHQpe2lmKGUuc3RhcnQueT5lLmVuZC55KXRocm93IG5ldyBFcnJvcigiQnVmZmVyIHJhbmdlIGVuZCAoIitlLmVuZC54KyIsICIrZS5lbmQueSsiKSBjYW5ub3QgYmUgYmVmb3JlIHN0YXJ0ICgiK2Uuc3RhcnQueCsiLCAiK2Uuc3RhcnQueSsiKSIpO3JldHVybiB0KihlLmVuZC55LWUuc3RhcnQueSkrKGUuZW5kLngtZS5zdGFydC54KzEpfX0sNDYzNDooZSx0KT0+e2Z1bmN0aW9uIHIoZSx0LHIpe2lmKHQ9PT1lLmxlbmd0aC0xKXJldHVybiBlW3RdLmdldFRyaW1tZWRMZW5ndGgoKTt2YXIgaT0hZVt0XS5oYXNDb250ZW50KHItMSkmJjE9PT1lW3RdLmdldFdpZHRoKHItMSksbj0yPT09ZVt0KzFdLmdldFdpZHRoKDApO3JldHVybiBpJiZuP3ItMTpyfU9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LmdldFdyYXBwZWRMaW5lVHJpbW1lZExlbmd0aD10LnJlZmxvd1NtYWxsZXJHZXROZXdMaW5lTGVuZ3Rocz10LnJlZmxvd0xhcmdlckFwcGx5TmV3TGF5b3V0PXQucmVmbG93TGFyZ2VyQ3JlYXRlTmV3TGF5b3V0PXQucmVmbG93TGFyZ2VyR2V0TGluZXNUb1JlbW92ZT12b2lkIDAsdC5yZWZsb3dMYXJnZXJHZXRMaW5lc1RvUmVtb3ZlPWZ1bmN0aW9uKGUsdCxpLG4sbyl7Zm9yKHZhciBzPVtdLGE9MDthPGUubGVuZ3RoLTE7YSsrKXt2YXIgYz1hLGw9ZS5nZXQoKytjKTtpZihsLmlzV3JhcHBlZCl7Zm9yKHZhciB1PVtlLmdldChhKV07YzxlLmxlbmd0aCYmbC5pc1dyYXBwZWQ7KXUucHVzaChsKSxsPWUuZ2V0KCsrYyk7aWYobj49YSYmbjxjKWErPXUubGVuZ3RoLTE7ZWxzZXtmb3IodmFyIGg9MCxmPXIodSxoLHQpLF89MSxkPTA7Xzx1Lmxlbmd0aDspe3ZhciBwPXIodSxfLHQpLHY9cC1kLGc9aS1mLHk9TWF0aC5taW4odixnKTt1W2hdLmNvcHlDZWxsc0Zyb20odVtfXSxkLGYseSwhMSksKGYrPXkpPT09aSYmKGgrKyxmPTApLChkKz15KT09PXAmJihfKyssZD0wKSwwPT09ZiYmMCE9PWgmJjI9PT11W2gtMV0uZ2V0V2lkdGgoaS0xKSYmKHVbaF0uY29weUNlbGxzRnJvbSh1W2gtMV0saS0xLGYrKywxLCExKSx1W2gtMV0uc2V0Q2VsbChpLTEsbykpfXVbaF0ucmVwbGFjZUNlbGxzKGYsaSxvKTtmb3IodmFyIG09MCxiPXUubGVuZ3RoLTE7Yj4wJiYoYj5ofHwwPT09dVtiXS5nZXRUcmltbWVkTGVuZ3RoKCkpO2ItLSltKys7bT4wJiYocy5wdXNoKGErdS5sZW5ndGgtbSkscy5wdXNoKG0pKSxhKz11Lmxlbmd0aC0xfX19cmV0dXJuIHN9LHQucmVmbG93TGFyZ2VyQ3JlYXRlTmV3TGF5b3V0PWZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByPVtdLGk9MCxuPXRbaV0sbz0wLHM9MDtzPGUubGVuZ3RoO3MrKylpZihuPT09cyl7dmFyIGE9dFsrK2ldO2Uub25EZWxldGVFbWl0dGVyLmZpcmUoe2luZGV4OnMtbyxhbW91bnQ6YX0pLHMrPWEtMSxvKz1hLG49dFsrK2ldfWVsc2Ugci5wdXNoKHMpO3JldHVybntsYXlvdXQ6cixjb3VudFJlbW92ZWQ6b319LHQucmVmbG93TGFyZ2VyQXBwbHlOZXdMYXlvdXQ9ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHI9W10saT0wO2k8dC5sZW5ndGg7aSsrKXIucHVzaChlLmdldCh0W2ldKSk7Zm9yKGk9MDtpPHIubGVuZ3RoO2krKyllLnNldChpLHJbaV0pO2UubGVuZ3RoPXQubGVuZ3RofSx0LnJlZmxvd1NtYWxsZXJHZXROZXdMaW5lTGVuZ3Rocz1mdW5jdGlvbihlLHQsaSl7Zm9yKHZhciBuPVtdLG89ZS5tYXAoKGZ1bmN0aW9uKGksbil7cmV0dXJuIHIoZSxuLHQpfSkpLnJlZHVjZSgoZnVuY3Rpb24oZSx0KXtyZXR1cm4gZSt0fSkpLHM9MCxhPTAsYz0wO2M8bzspe2lmKG8tYzxpKXtuLnB1c2goby1jKTticmVha31zKz1pO3ZhciBsPXIoZSxhLHQpO3M+bCYmKHMtPWwsYSsrKTt2YXIgdT0yPT09ZVthXS5nZXRXaWR0aChzLTEpO3UmJnMtLTt2YXIgaD11P2ktMTppO24ucHVzaChoKSxjKz1ofXJldHVybiBufSx0LmdldFdyYXBwZWRMaW5lVHJpbW1lZExlbmd0aD1yfSw1Mjk1OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJ1ZmZlclNldD12b2lkIDA7dmFyIG89cig5MDkyKSxzPXIoODQ2MCksYT1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscil7dmFyIGk9ZS5jYWxsKHRoaXMpfHx0aGlzO3JldHVybiBpLl9vcHRpb25zU2VydmljZT10LGkuX2J1ZmZlclNlcnZpY2U9cixpLl9vbkJ1ZmZlckFjdGl2YXRlPWkucmVnaXN0ZXIobmV3IHMuRXZlbnRFbWl0dGVyKSxpLnJlc2V0KCksaX1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25CdWZmZXJBY3RpdmF0ZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9vbkJ1ZmZlckFjdGl2YXRlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLHQucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5fbm9ybWFsPW5ldyBvLkJ1ZmZlcighMCx0aGlzLl9vcHRpb25zU2VydmljZSx0aGlzLl9idWZmZXJTZXJ2aWNlKSx0aGlzLl9ub3JtYWwuZmlsbFZpZXdwb3J0Um93cygpLHRoaXMuX2FsdD1uZXcgby5CdWZmZXIoITEsdGhpcy5fb3B0aW9uc1NlcnZpY2UsdGhpcy5fYnVmZmVyU2VydmljZSksdGhpcy5fYWN0aXZlQnVmZmVyPXRoaXMuX25vcm1hbCx0aGlzLl9vbkJ1ZmZlckFjdGl2YXRlLmZpcmUoe2FjdGl2ZUJ1ZmZlcjp0aGlzLl9ub3JtYWwsaW5hY3RpdmVCdWZmZXI6dGhpcy5fYWx0fSksdGhpcy5zZXR1cFRhYlN0b3BzKCl9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwiYWx0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2FsdH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsImFjdGl2ZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9hY3RpdmVCdWZmZXJ9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJub3JtYWwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fbm9ybWFsfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLHQucHJvdG90eXBlLmFjdGl2YXRlTm9ybWFsQnVmZmVyPWZ1bmN0aW9uKCl7dGhpcy5fYWN0aXZlQnVmZmVyIT09dGhpcy5fbm9ybWFsJiYodGhpcy5fbm9ybWFsLng9dGhpcy5fYWx0LngsdGhpcy5fbm9ybWFsLnk9dGhpcy5fYWx0LnksdGhpcy5fYWx0LmNsZWFyKCksdGhpcy5fYWN0aXZlQnVmZmVyPXRoaXMuX25vcm1hbCx0aGlzLl9vbkJ1ZmZlckFjdGl2YXRlLmZpcmUoe2FjdGl2ZUJ1ZmZlcjp0aGlzLl9ub3JtYWwsaW5hY3RpdmVCdWZmZXI6dGhpcy5fYWx0fSkpfSx0LnByb3RvdHlwZS5hY3RpdmF0ZUFsdEJ1ZmZlcj1mdW5jdGlvbihlKXt0aGlzLl9hY3RpdmVCdWZmZXIhPT10aGlzLl9hbHQmJih0aGlzLl9hbHQuZmlsbFZpZXdwb3J0Um93cyhlKSx0aGlzLl9hbHQueD10aGlzLl9ub3JtYWwueCx0aGlzLl9hbHQueT10aGlzLl9ub3JtYWwueSx0aGlzLl9hY3RpdmVCdWZmZXI9dGhpcy5fYWx0LHRoaXMuX29uQnVmZmVyQWN0aXZhdGUuZmlyZSh7YWN0aXZlQnVmZmVyOnRoaXMuX2FsdCxpbmFjdGl2ZUJ1ZmZlcjp0aGlzLl9ub3JtYWx9KSl9LHQucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuX25vcm1hbC5yZXNpemUoZSx0KSx0aGlzLl9hbHQucmVzaXplKGUsdCl9LHQucHJvdG90eXBlLnNldHVwVGFiU3RvcHM9ZnVuY3Rpb24oZSl7dGhpcy5fbm9ybWFsLnNldHVwVGFiU3RvcHMoZSksdGhpcy5fYWx0LnNldHVwVGFiU3RvcHMoZSl9LHR9KHIoODQ0KS5EaXNwb3NhYmxlKTt0LkJ1ZmZlclNldD1hfSw1MTE6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQ2VsbERhdGE9dm9pZCAwO3ZhciBvPXIoNDgyKSxzPXIoNjQzKSxhPXIoMzczNCksYz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KCl7dmFyIHQ9bnVsbCE9PWUmJmUuYXBwbHkodGhpcyxhcmd1bWVudHMpfHx0aGlzO3JldHVybiB0LmNvbnRlbnQ9MCx0LmZnPTAsdC5iZz0wLHQuZXh0ZW5kZWQ9bmV3IGEuRXh0ZW5kZWRBdHRycyx0LmNvbWJpbmVkRGF0YT0iIix0fXJldHVybiBuKHQsZSksdC5mcm9tQ2hhckRhdGE9ZnVuY3Rpb24oZSl7dmFyIHI9bmV3IHQ7cmV0dXJuIHIuc2V0RnJvbUNoYXJEYXRhKGUpLHJ9LHQucHJvdG90eXBlLmlzQ29tYmluZWQ9ZnVuY3Rpb24oKXtyZXR1cm4gMjA5NzE1MiZ0aGlzLmNvbnRlbnR9LHQucHJvdG90eXBlLmdldFdpZHRoPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuY29udGVudD4+MjJ9LHQucHJvdG90eXBlLmdldENoYXJzPWZ1bmN0aW9uKCl7cmV0dXJuIDIwOTcxNTImdGhpcy5jb250ZW50P3RoaXMuY29tYmluZWREYXRhOjIwOTcxNTEmdGhpcy5jb250ZW50PygwLG8uc3RyaW5nRnJvbUNvZGVQb2ludCkoMjA5NzE1MSZ0aGlzLmNvbnRlbnQpOiIifSx0LnByb3RvdHlwZS5nZXRDb2RlPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuaXNDb21iaW5lZCgpP3RoaXMuY29tYmluZWREYXRhLmNoYXJDb2RlQXQodGhpcy5jb21iaW5lZERhdGEubGVuZ3RoLTEpOjIwOTcxNTEmdGhpcy5jb250ZW50fSx0LnByb3RvdHlwZS5zZXRGcm9tQ2hhckRhdGE9ZnVuY3Rpb24oZSl7dGhpcy5mZz1lW3MuQ0hBUl9EQVRBX0FUVFJfSU5ERVhdLHRoaXMuYmc9MDt2YXIgdD0hMTtpZihlW3MuQ0hBUl9EQVRBX0NIQVJfSU5ERVhdLmxlbmd0aD4yKXQ9ITA7ZWxzZSBpZigyPT09ZVtzLkNIQVJfREFUQV9DSEFSX0lOREVYXS5sZW5ndGgpe3ZhciByPWVbcy5DSEFSX0RBVEFfQ0hBUl9JTkRFWF0uY2hhckNvZGVBdCgwKTtpZig1NTI5Njw9ciYmcjw9NTYzMTkpe3ZhciBpPWVbcy5DSEFSX0RBVEFfQ0hBUl9JTkRFWF0uY2hhckNvZGVBdCgxKTs1NjMyMDw9aSYmaTw9NTczNDM/dGhpcy5jb250ZW50PTEwMjQqKHItNTUyOTYpK2ktNTYzMjArNjU1MzZ8ZVtzLkNIQVJfREFUQV9XSURUSF9JTkRFWF08PDIyOnQ9ITB9ZWxzZSB0PSEwfWVsc2UgdGhpcy5jb250ZW50PWVbcy5DSEFSX0RBVEFfQ0hBUl9JTkRFWF0uY2hhckNvZGVBdCgwKXxlW3MuQ0hBUl9EQVRBX1dJRFRIX0lOREVYXTw8MjI7dCYmKHRoaXMuY29tYmluZWREYXRhPWVbcy5DSEFSX0RBVEFfQ0hBUl9JTkRFWF0sdGhpcy5jb250ZW50PTIwOTcxNTJ8ZVtzLkNIQVJfREFUQV9XSURUSF9JTkRFWF08PDIyKX0sdC5wcm90b3R5cGUuZ2V0QXNDaGFyRGF0YT1mdW5jdGlvbigpe3JldHVyblt0aGlzLmZnLHRoaXMuZ2V0Q2hhcnMoKSx0aGlzLmdldFdpZHRoKCksdGhpcy5nZXRDb2RlKCldfSx0fShhLkF0dHJpYnV0ZURhdGEpO3QuQ2VsbERhdGE9Y30sNjQzOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuV0hJVEVTUEFDRV9DRUxMX0NPREU9dC5XSElURVNQQUNFX0NFTExfV0lEVEg9dC5XSElURVNQQUNFX0NFTExfQ0hBUj10Lk5VTExfQ0VMTF9DT0RFPXQuTlVMTF9DRUxMX1dJRFRIPXQuTlVMTF9DRUxMX0NIQVI9dC5DSEFSX0RBVEFfQ09ERV9JTkRFWD10LkNIQVJfREFUQV9XSURUSF9JTkRFWD10LkNIQVJfREFUQV9DSEFSX0lOREVYPXQuQ0hBUl9EQVRBX0FUVFJfSU5ERVg9dC5ERUZBVUxUX0FUVFI9dC5ERUZBVUxUX0NPTE9SPXZvaWQgMCx0LkRFRkFVTFRfQ09MT1I9MjU2LHQuREVGQVVMVF9BVFRSPTI1Nnx0LkRFRkFVTFRfQ09MT1I8PDksdC5DSEFSX0RBVEFfQVRUUl9JTkRFWD0wLHQuQ0hBUl9EQVRBX0NIQVJfSU5ERVg9MSx0LkNIQVJfREFUQV9XSURUSF9JTkRFWD0yLHQuQ0hBUl9EQVRBX0NPREVfSU5ERVg9Myx0Lk5VTExfQ0VMTF9DSEFSPSIiLHQuTlVMTF9DRUxMX1dJRFRIPTEsdC5OVUxMX0NFTExfQ09ERT0wLHQuV0hJVEVTUEFDRV9DRUxMX0NIQVI9IiAiLHQuV0hJVEVTUEFDRV9DRUxMX1dJRFRIPTEsdC5XSElURVNQQUNFX0NFTExfQ09ERT0zMn0sNDg2MzpmdW5jdGlvbihlLHQscil7dmFyIGksbj10aGlzJiZ0aGlzLl9fZXh0ZW5kc3x8KGk9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gaT1PYmplY3Quc2V0UHJvdG90eXBlT2Z8fHtfX3Byb3RvX186W119aW5zdGFuY2VvZiBBcnJheSYmZnVuY3Rpb24oZSx0KXtlLl9fcHJvdG9fXz10fXx8ZnVuY3Rpb24oZSx0KXtmb3IodmFyIHIgaW4gdClPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwodCxyKSYmKGVbcl09dFtyXSl9LGkoZSx0KX0sZnVuY3Rpb24oZSx0KXtpZigiZnVuY3Rpb24iIT10eXBlb2YgdCYmbnVsbCE9PXQpdGhyb3cgbmV3IFR5cGVFcnJvcigiQ2xhc3MgZXh0ZW5kcyB2YWx1ZSAiK1N0cmluZyh0KSsiIGlzIG5vdCBhIGNvbnN0cnVjdG9yIG9yIG51bGwiKTtmdW5jdGlvbiByKCl7dGhpcy5jb25zdHJ1Y3Rvcj1lfWkoZSx0KSxlLnByb3RvdHlwZT1udWxsPT09dD9PYmplY3QuY3JlYXRlKHQpOihyLnByb3RvdHlwZT10LnByb3RvdHlwZSxuZXcgcil9KTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5NYXJrZXI9dm9pZCAwO3ZhciBvPXIoODQ2MCkscz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHIpe3ZhciBpPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gaS5saW5lPXIsaS5faWQ9dC5fbmV4dElkKyssaS5pc0Rpc3Bvc2VkPSExLGkuX29uRGlzcG9zZT1uZXcgby5FdmVudEVtaXR0ZXIsaX1yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwiaWQiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5faWR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHQucHJvdG90eXBlLCJvbkRpc3Bvc2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25EaXNwb3NlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLHQucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXt0aGlzLmlzRGlzcG9zZWR8fCh0aGlzLmlzRGlzcG9zZWQ9ITAsdGhpcy5saW5lPS0xLHRoaXMuX29uRGlzcG9zZS5maXJlKCksZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpKX0sdC5fbmV4dElkPTEsdH0ocig4NDQpLkRpc3Bvc2FibGUpO3QuTWFya2VyPXN9LDcxMTY6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5ERUZBVUxUX0NIQVJTRVQ9dC5DSEFSU0VUUz12b2lkIDAsdC5DSEFSU0VUUz17fSx0LkRFRkFVTFRfQ0hBUlNFVD10LkNIQVJTRVRTLkIsdC5DSEFSU0VUU1swXT17ImAiOiLil4YiLGE6IuKWkiIsYjoi4pCJIixjOiLikIwiLGQ6IuKQjSIsZToi4pCKIixmOiLCsCIsZzoiwrEiLGg6IuKQpCIsaToi4pCLIixqOiLilJgiLGs6IuKUkCIsbDoi4pSMIixtOiLilJQiLG46IuKUvCIsbzoi4o66IixwOiLijrsiLHE6IuKUgCIscjoi4o68IixzOiLijr0iLHQ6IuKUnCIsdToi4pSkIix2OiLilLQiLHc6IuKUrCIseDoi4pSCIix5OiLiiaQiLHo6IuKJpSIsInsiOiLPgCIsInwiOiLiiaAiLCJ9IjoiwqMiLCJ+IjoiwrcifSx0LkNIQVJTRVRTLkE9eyIjIjoiwqMifSx0LkNIQVJTRVRTLkI9dm9pZCAwLHQuQ0hBUlNFVFNbNF09eyIjIjoiwqMiLCJAIjoiwr4iLCJbIjoiaWoiLCJcXCI6IsK9IiwiXSI6InwiLCJ7IjoiwqgiLCJ8IjoiZiIsIn0iOiLCvCIsIn4iOiLCtCJ9LHQuQ0hBUlNFVFMuQz10LkNIQVJTRVRTWzVdPXsiWyI6IsOEIiwiXFwiOiLDliIsIl0iOiLDhSIsIl4iOiLDnCIsImAiOiLDqSIsInsiOiLDpCIsInwiOiLDtiIsIn0iOiLDpSIsIn4iOiLDvCJ9LHQuQ0hBUlNFVFMuUj17IiMiOiLCoyIsIkAiOiLDoCIsIlsiOiLCsCIsIlxcIjoiw6ciLCJdIjoiwqciLCJ7Ijoiw6kiLCJ8Ijoiw7kiLCJ9Ijoiw6giLCJ+IjoiwqgifSx0LkNIQVJTRVRTLlE9eyJAIjoiw6AiLCJbIjoiw6IiLCJcXCI6IsOnIiwiXSI6IsOqIiwiXiI6IsOuIiwiYCI6IsO0IiwieyI6IsOpIiwifCI6IsO5IiwifSI6IsOoIiwifiI6IsO7In0sdC5DSEFSU0VUUy5LPXsiQCI6IsKnIiwiWyI6IsOEIiwiXFwiOiLDliIsIl0iOiLDnCIsInsiOiLDpCIsInwiOiLDtiIsIn0iOiLDvCIsIn4iOiLDnyJ9LHQuQ0hBUlNFVFMuWT17IiMiOiLCoyIsIkAiOiLCpyIsIlsiOiLCsCIsIlxcIjoiw6ciLCJdIjoiw6kiLCJgIjoiw7kiLCJ7Ijoiw6AiLCJ8Ijoiw7IiLCJ9Ijoiw6giLCJ+Ijoiw6wifSx0LkNIQVJTRVRTLkU9dC5DSEFSU0VUU1s2XT17IkAiOiLDhCIsIlsiOiLDhiIsIlxcIjoiw5giLCJdIjoiw4UiLCJeIjoiw5wiLCJgIjoiw6QiLCJ7Ijoiw6YiLCJ8Ijoiw7giLCJ9Ijoiw6UiLCJ+Ijoiw7wifSx0LkNIQVJTRVRTLlo9eyIjIjoiwqMiLCJAIjoiwqciLCJbIjoiwqEiLCJcXCI6IsORIiwiXSI6IsK/IiwieyI6IsKwIiwifCI6IsOxIiwifSI6IsOnIn0sdC5DSEFSU0VUUy5IPXQuQ0hBUlNFVFNbN109eyJAIjoiw4kiLCJbIjoiw4QiLCJcXCI6IsOWIiwiXSI6IsOFIiwiXiI6IsOcIiwiYCI6IsOpIiwieyI6IsOkIiwifCI6IsO2IiwifSI6IsOlIiwifiI6IsO8In0sdC5DSEFSU0VUU1siPSJdPXsiIyI6IsO5IiwiQCI6IsOgIiwiWyI6IsOpIiwiXFwiOiLDpyIsIl0iOiLDqiIsIl4iOiLDriIsXzoiw6giLCJgIjoiw7QiLCJ7Ijoiw6QiLCJ8Ijoiw7YiLCJ9Ijoiw7wiLCJ+Ijoiw7sifX0sMjU4NDooZSx0KT0+e3ZhciByLGk7T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQzE9dC5DMD12b2lkIDAsKGk9dC5DMHx8KHQuQzA9e30pKS5OVUw9IlwwIixpLlNPSD0iASIsaS5TVFg9IgIiLGkuRVRYPSIDIixpLkVPVD0iBCIsaS5FTlE9IgUiLGkuQUNLPSIGIixpLkJFTD0iByIsaS5CUz0iXGIiLGkuSFQ9Ilx0IixpLkxGPSJcbiIsaS5WVD0iXHYiLGkuRkY9IlxmIixpLkNSPSJcciIsaS5TTz0iDiIsaS5TST0iDyIsaS5ETEU9IhAiLGkuREMxPSIRIixpLkRDMj0iEiIsaS5EQzM9IhMiLGkuREM0PSIUIixpLk5BSz0iFSIsaS5TWU49IhYiLGkuRVRCPSIXIixpLkNBTj0iGCIsaS5FTT0iGSIsaS5TVUI9IhoiLGkuRVNDPSIbIixpLkZTPSIcIixpLkdTPSIdIixpLlJTPSIeIixpLlVTPSIfIixpLlNQPSIgIixpLkRFTD0ifyIsKHI9dC5DMXx8KHQuQzE9e30pKS5QQUQ9IsKAIixyLkhPUD0iwoEiLHIuQlBIPSLCgiIsci5OQkg9IsKDIixyLklORD0iwoQiLHIuTkVMPSLChSIsci5TU0E9IsKGIixyLkVTQT0iwociLHIuSFRTPSLCiCIsci5IVEo9IsKJIixyLlZUUz0iwooiLHIuUExEPSLCiyIsci5QTFU9IsKMIixyLlJJPSLCjSIsci5TUzI9IsKOIixyLlNTMz0iwo8iLHIuRENTPSLCkCIsci5QVTE9IsKRIixyLlBVMj0iwpIiLHIuU1RTPSLCkyIsci5DQ0g9IsKUIixyLk1XPSLClSIsci5TUEE9IsKWIixyLkVQQT0iwpciLHIuU09TPSLCmCIsci5TR0NJPSLCmSIsci5TQ0k9IsKaIixyLkNTST0iwpsiLHIuU1Q9IsKcIixyLk9TQz0iwp0iLHIuUE09IsKeIixyLkFQQz0iwp8ifSw3Mzk5OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5ldmFsdWF0ZUtleWJvYXJkRXZlbnQ9dm9pZCAwO3ZhciBpPXIoMjU4NCksbj17NDg6WyIwIiwiKSJdLDQ5OlsiMSIsIiEiXSw1MDpbIjIiLCJAIl0sNTE6WyIzIiwiIyJdLDUyOlsiNCIsIiQiXSw1MzpbIjUiLCIlIl0sNTQ6WyI2IiwiXiJdLDU1OlsiNyIsIiYiXSw1NjpbIjgiLCIqIl0sNTc6WyI5IiwiKCJdLDE4NjpbIjsiLCI6Il0sMTg3OlsiPSIsIisiXSwxODg6WyIsIiwiPCJdLDE4OTpbIi0iLCJfIl0sMTkwOlsiLiIsIj4iXSwxOTE6WyIvIiwiPyJdLDE5MjpbImAiLCJ+Il0sMjE5OlsiWyIsInsiXSwyMjA6WyJcXCIsInwiXSwyMjE6WyJdIiwifSJdLDIyMjpbIiciLCciJ119O3QuZXZhbHVhdGVLZXlib2FyZEV2ZW50PWZ1bmN0aW9uKGUsdCxyLG8pe3ZhciBzPXt0eXBlOjAsY2FuY2VsOiExLGtleTp2b2lkIDB9LGE9KGUuc2hpZnRLZXk/MTowKXwoZS5hbHRLZXk/MjowKXwoZS5jdHJsS2V5PzQ6MCl8KGUubWV0YUtleT84OjApO3N3aXRjaChlLmtleUNvZGUpe2Nhc2UgMDoiVUlLZXlJbnB1dFVwQXJyb3ciPT09ZS5rZXk/cy5rZXk9dD9pLkMwLkVTQysiT0EiOmkuQzAuRVNDKyJbQSI6IlVJS2V5SW5wdXRMZWZ0QXJyb3ciPT09ZS5rZXk/cy5rZXk9dD9pLkMwLkVTQysiT0QiOmkuQzAuRVNDKyJbRCI6IlVJS2V5SW5wdXRSaWdodEFycm93Ij09PWUua2V5P3Mua2V5PXQ/aS5DMC5FU0MrIk9DIjppLkMwLkVTQysiW0MiOiJVSUtleUlucHV0RG93bkFycm93Ij09PWUua2V5JiYocy5rZXk9dD9pLkMwLkVTQysiT0IiOmkuQzAuRVNDKyJbQiIpO2JyZWFrO2Nhc2UgODppZihlLnNoaWZ0S2V5KXtzLmtleT1pLkMwLkJTO2JyZWFrfWlmKGUuYWx0S2V5KXtzLmtleT1pLkMwLkVTQytpLkMwLkRFTDticmVha31zLmtleT1pLkMwLkRFTDticmVhaztjYXNlIDk6aWYoZS5zaGlmdEtleSl7cy5rZXk9aS5DMC5FU0MrIltaIjticmVha31zLmtleT1pLkMwLkhULHMuY2FuY2VsPSEwO2JyZWFrO2Nhc2UgMTM6cy5rZXk9ZS5hbHRLZXk/aS5DMC5FU0MraS5DMC5DUjppLkMwLkNSLHMuY2FuY2VsPSEwO2JyZWFrO2Nhc2UgMjc6cy5rZXk9aS5DMC5FU0MsZS5hbHRLZXkmJihzLmtleT1pLkMwLkVTQytpLkMwLkVTQykscy5jYW5jZWw9ITA7YnJlYWs7Y2FzZSAzNzppZihlLm1ldGFLZXkpYnJlYWs7YT8ocy5rZXk9aS5DMC5FU0MrIlsxOyIrKGErMSkrIkQiLHMua2V5PT09aS5DMC5FU0MrIlsxOzNEIiYmKHMua2V5PWkuQzAuRVNDKyhyPyJiIjoiWzE7NUQiKSkpOnMua2V5PXQ/aS5DMC5FU0MrIk9EIjppLkMwLkVTQysiW0QiO2JyZWFrO2Nhc2UgMzk6aWYoZS5tZXRhS2V5KWJyZWFrO2E/KHMua2V5PWkuQzAuRVNDKyJbMTsiKyhhKzEpKyJDIixzLmtleT09PWkuQzAuRVNDKyJbMTszQyImJihzLmtleT1pLkMwLkVTQysocj8iZiI6IlsxOzVDIikpKTpzLmtleT10P2kuQzAuRVNDKyJPQyI6aS5DMC5FU0MrIltDIjticmVhaztjYXNlIDM4OmlmKGUubWV0YUtleSlicmVhazthPyhzLmtleT1pLkMwLkVTQysiWzE7IisoYSsxKSsiQSIscnx8cy5rZXkhPT1pLkMwLkVTQysiWzE7M0EifHwocy5rZXk9aS5DMC5FU0MrIlsxOzVBIikpOnMua2V5PXQ/aS5DMC5FU0MrIk9BIjppLkMwLkVTQysiW0EiO2JyZWFrO2Nhc2UgNDA6aWYoZS5tZXRhS2V5KWJyZWFrO2E/KHMua2V5PWkuQzAuRVNDKyJbMTsiKyhhKzEpKyJCIixyfHxzLmtleSE9PWkuQzAuRVNDKyJbMTszQiJ8fChzLmtleT1pLkMwLkVTQysiWzE7NUIiKSk6cy5rZXk9dD9pLkMwLkVTQysiT0IiOmkuQzAuRVNDKyJbQiI7YnJlYWs7Y2FzZSA0NTplLnNoaWZ0S2V5fHxlLmN0cmxLZXl8fChzLmtleT1pLkMwLkVTQysiWzJ+Iik7YnJlYWs7Y2FzZSA0NjpzLmtleT1hP2kuQzAuRVNDKyJbMzsiKyhhKzEpKyJ+IjppLkMwLkVTQysiWzN+IjticmVhaztjYXNlIDM2OnMua2V5PWE/aS5DMC5FU0MrIlsxOyIrKGErMSkrIkgiOnQ/aS5DMC5FU0MrIk9IIjppLkMwLkVTQysiW0giO2JyZWFrO2Nhc2UgMzU6cy5rZXk9YT9pLkMwLkVTQysiWzE7IisoYSsxKSsiRiI6dD9pLkMwLkVTQysiT0YiOmkuQzAuRVNDKyJbRiI7YnJlYWs7Y2FzZSAzMzplLnNoaWZ0S2V5P3MudHlwZT0yOnMua2V5PWkuQzAuRVNDKyJbNX4iO2JyZWFrO2Nhc2UgMzQ6ZS5zaGlmdEtleT9zLnR5cGU9MzpzLmtleT1pLkMwLkVTQysiWzZ+IjticmVhaztjYXNlIDExMjpzLmtleT1hP2kuQzAuRVNDKyJbMTsiKyhhKzEpKyJQIjppLkMwLkVTQysiT1AiO2JyZWFrO2Nhc2UgMTEzOnMua2V5PWE/aS5DMC5FU0MrIlsxOyIrKGErMSkrIlEiOmkuQzAuRVNDKyJPUSI7YnJlYWs7Y2FzZSAxMTQ6cy5rZXk9YT9pLkMwLkVTQysiWzE7IisoYSsxKSsiUiI6aS5DMC5FU0MrIk9SIjticmVhaztjYXNlIDExNTpzLmtleT1hP2kuQzAuRVNDKyJbMTsiKyhhKzEpKyJTIjppLkMwLkVTQysiT1MiO2JyZWFrO2Nhc2UgMTE2OnMua2V5PWE/aS5DMC5FU0MrIlsxNTsiKyhhKzEpKyJ+IjppLkMwLkVTQysiWzE1fiI7YnJlYWs7Y2FzZSAxMTc6cy5rZXk9YT9pLkMwLkVTQysiWzE3OyIrKGErMSkrIn4iOmkuQzAuRVNDKyJbMTd+IjticmVhaztjYXNlIDExODpzLmtleT1hP2kuQzAuRVNDKyJbMTg7IisoYSsxKSsifiI6aS5DMC5FU0MrIlsxOH4iO2JyZWFrO2Nhc2UgMTE5OnMua2V5PWE/aS5DMC5FU0MrIlsxOTsiKyhhKzEpKyJ+IjppLkMwLkVTQysiWzE5fiI7YnJlYWs7Y2FzZSAxMjA6cy5rZXk9YT9pLkMwLkVTQysiWzIwOyIrKGErMSkrIn4iOmkuQzAuRVNDKyJbMjB+IjticmVhaztjYXNlIDEyMTpzLmtleT1hP2kuQzAuRVNDKyJbMjE7IisoYSsxKSsifiI6aS5DMC5FU0MrIlsyMX4iO2JyZWFrO2Nhc2UgMTIyOnMua2V5PWE/aS5DMC5FU0MrIlsyMzsiKyhhKzEpKyJ+IjppLkMwLkVTQysiWzIzfiI7YnJlYWs7Y2FzZSAxMjM6cy5rZXk9YT9pLkMwLkVTQysiWzI0OyIrKGErMSkrIn4iOmkuQzAuRVNDKyJbMjR+IjticmVhaztkZWZhdWx0OmlmKCFlLmN0cmxLZXl8fGUuc2hpZnRLZXl8fGUuYWx0S2V5fHxlLm1ldGFLZXkpaWYociYmIW98fCFlLmFsdEtleXx8ZS5tZXRhS2V5KSFyfHxlLmFsdEtleXx8ZS5jdHJsS2V5fHxlLnNoaWZ0S2V5fHwhZS5tZXRhS2V5P2Uua2V5JiYhZS5jdHJsS2V5JiYhZS5hbHRLZXkmJiFlLm1ldGFLZXkmJmUua2V5Q29kZT49NDgmJjE9PT1lLmtleS5sZW5ndGg/cy5rZXk9ZS5rZXk6ZS5rZXkmJmUuY3RybEtleSYmIl8iPT09ZS5rZXkmJihzLmtleT1pLkMwLlVTKTo2NT09PWUua2V5Q29kZSYmKHMudHlwZT0xKTtlbHNle3ZhciBjPW5bZS5rZXlDb2RlXSxsPW51bGw9PWM/dm9pZCAwOmNbZS5zaGlmdEtleT8xOjBdO2lmKGwpcy5rZXk9aS5DMC5FU0MrbDtlbHNlIGlmKGUua2V5Q29kZT49NjUmJmUua2V5Q29kZTw9OTApe3ZhciB1PWUuY3RybEtleT9lLmtleUNvZGUtNjQ6ZS5rZXlDb2RlKzMyO3Mua2V5PWkuQzAuRVNDK1N0cmluZy5mcm9tQ2hhckNvZGUodSl9fWVsc2UgZS5rZXlDb2RlPj02NSYmZS5rZXlDb2RlPD05MD9zLmtleT1TdHJpbmcuZnJvbUNoYXJDb2RlKGUua2V5Q29kZS02NCk6MzI9PT1lLmtleUNvZGU/cy5rZXk9aS5DMC5OVUw6ZS5rZXlDb2RlPj01MSYmZS5rZXlDb2RlPD01NT9zLmtleT1TdHJpbmcuZnJvbUNoYXJDb2RlKGUua2V5Q29kZS01MSsyNyk6NTY9PT1lLmtleUNvZGU/cy5rZXk9aS5DMC5ERUw6MjE5PT09ZS5rZXlDb2RlP3Mua2V5PWkuQzAuRVNDOjIyMD09PWUua2V5Q29kZT9zLmtleT1pLkMwLkZTOjIyMT09PWUua2V5Q29kZSYmKHMua2V5PWkuQzAuR1MpfXJldHVybiBzfX0sNDgyOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuVXRmOFRvVXRmMzI9dC5TdHJpbmdUb1V0ZjMyPXQudXRmMzJUb1N0cmluZz10LnN0cmluZ0Zyb21Db2RlUG9pbnQ9dm9pZCAwLHQuc3RyaW5nRnJvbUNvZGVQb2ludD1mdW5jdGlvbihlKXtyZXR1cm4gZT42NTUzNT8oZS09NjU1MzYsU3RyaW5nLmZyb21DaGFyQ29kZSg1NTI5NisoZT4+MTApKStTdHJpbmcuZnJvbUNoYXJDb2RlKGUlMTAyNCs1NjMyMCkpOlN0cmluZy5mcm9tQ2hhckNvZGUoZSl9LHQudXRmMzJUb1N0cmluZz1mdW5jdGlvbihlLHQscil7dm9pZCAwPT09dCYmKHQ9MCksdm9pZCAwPT09ciYmKHI9ZS5sZW5ndGgpO2Zvcih2YXIgaT0iIixuPXQ7bjxyOysrbil7dmFyIG89ZVtuXTtvPjY1NTM1PyhvLT02NTUzNixpKz1TdHJpbmcuZnJvbUNoYXJDb2RlKDU1Mjk2KyhvPj4xMCkpK1N0cmluZy5mcm9tQ2hhckNvZGUobyUxMDI0KzU2MzIwKSk6aSs9U3RyaW5nLmZyb21DaGFyQ29kZShvKX1yZXR1cm4gaX07dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKCl7dGhpcy5faW50ZXJpbT0wfXJldHVybiBlLnByb3RvdHlwZS5jbGVhcj1mdW5jdGlvbigpe3RoaXMuX2ludGVyaW09MH0sZS5wcm90b3R5cGUuZGVjb2RlPWZ1bmN0aW9uKGUsdCl7dmFyIHI9ZS5sZW5ndGg7aWYoIXIpcmV0dXJuIDA7dmFyIGk9MCxuPTA7dGhpcy5faW50ZXJpbSYmKDU2MzIwPD0oYT1lLmNoYXJDb2RlQXQobisrKSkmJmE8PTU3MzQzP3RbaSsrXT0xMDI0Kih0aGlzLl9pbnRlcmltLTU1Mjk2KSthLTU2MzIwKzY1NTM2Oih0W2krK109dGhpcy5faW50ZXJpbSx0W2krK109YSksdGhpcy5faW50ZXJpbT0wKTtmb3IodmFyIG89bjtvPHI7KytvKXt2YXIgcz1lLmNoYXJDb2RlQXQobyk7aWYoNTUyOTY8PXMmJnM8PTU2MzE5KXtpZigrK28+PXIpcmV0dXJuIHRoaXMuX2ludGVyaW09cyxpO3ZhciBhOzU2MzIwPD0oYT1lLmNoYXJDb2RlQXQobykpJiZhPD01NzM0Mz90W2krK109MTAyNCoocy01NTI5NikrYS01NjMyMCs2NTUzNjoodFtpKytdPXMsdFtpKytdPWEpfWVsc2UgNjUyNzkhPT1zJiYodFtpKytdPXMpfXJldHVybiBpfSxlfSgpO3QuU3RyaW5nVG9VdGYzMj1yO3ZhciBpPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuaW50ZXJpbT1uZXcgVWludDhBcnJheSgzKX1yZXR1cm4gZS5wcm90b3R5cGUuY2xlYXI9ZnVuY3Rpb24oKXt0aGlzLmludGVyaW0uZmlsbCgwKX0sZS5wcm90b3R5cGUuZGVjb2RlPWZ1bmN0aW9uKGUsdCl7dmFyIHI9ZS5sZW5ndGg7aWYoIXIpcmV0dXJuIDA7dmFyIGksbixvLHMsYT0wLGM9MCxsPTA7aWYodGhpcy5pbnRlcmltWzBdKXt2YXIgdT0hMSxoPXRoaXMuaW50ZXJpbVswXTtoJj0xOTI9PSgyMjQmaCk/MzE6MjI0PT0oMjQwJmgpPzE1Ojc7Zm9yKHZhciBmPTAsXz12b2lkIDA7KF89NjMmdGhpcy5pbnRlcmltWysrZl0pJiZmPDQ7KWg8PD02LGh8PV87Zm9yKHZhciBkPTE5Mj09KDIyNCZ0aGlzLmludGVyaW1bMF0pPzI6MjI0PT0oMjQwJnRoaXMuaW50ZXJpbVswXSk/Mzo0LHA9ZC1mO2w8cDspe2lmKGw+PXIpcmV0dXJuIDA7aWYoMTI4IT0oMTkyJihfPWVbbCsrXSkpKXtsLS0sdT0hMDticmVha310aGlzLmludGVyaW1bZisrXT1fLGg8PD02LGh8PTYzJl99dXx8KDI9PT1kP2g8MTI4P2wtLTp0W2ErK109aDozPT09ZD9oPDIwNDh8fGg+PTU1Mjk2JiZoPD01NzM0M3x8NjUyNzk9PT1ofHwodFthKytdPWgpOmg8NjU1MzZ8fGg+MTExNDExMXx8KHRbYSsrXT1oKSksdGhpcy5pbnRlcmltLmZpbGwoMCl9Zm9yKHZhciB2PXItNCxnPWw7ZzxyOyl7Zm9yKDshKCEoZzx2KXx8MTI4JihpPWVbZ10pfHwxMjgmKG49ZVtnKzFdKXx8MTI4JihvPWVbZysyXSl8fDEyOCYocz1lW2crM10pKTspdFthKytdPWksdFthKytdPW4sdFthKytdPW8sdFthKytdPXMsZys9NDtpZigoaT1lW2crK10pPDEyOCl0W2ErK109aTtlbHNlIGlmKDE5Mj09KDIyNCZpKSl7aWYoZz49cilyZXR1cm4gdGhpcy5pbnRlcmltWzBdPWksYTtpZigxMjghPSgxOTImKG49ZVtnKytdKSkpe2ctLTtjb250aW51ZX1pZigoYz0oMzEmaSk8PDZ8NjMmbik8MTI4KXtnLS07Y29udGludWV9dFthKytdPWN9ZWxzZSBpZigyMjQ9PSgyNDAmaSkpe2lmKGc+PXIpcmV0dXJuIHRoaXMuaW50ZXJpbVswXT1pLGE7aWYoMTI4IT0oMTkyJihuPWVbZysrXSkpKXtnLS07Y29udGludWV9aWYoZz49cilyZXR1cm4gdGhpcy5pbnRlcmltWzBdPWksdGhpcy5pbnRlcmltWzFdPW4sYTtpZigxMjghPSgxOTImKG89ZVtnKytdKSkpe2ctLTtjb250aW51ZX1pZigoYz0oMTUmaSk8PDEyfCg2MyZuKTw8Nnw2MyZvKTwyMDQ4fHxjPj01NTI5NiYmYzw9NTczNDN8fDY1Mjc5PT09Yyljb250aW51ZTt0W2ErK109Y31lbHNlIGlmKDI0MD09KDI0OCZpKSl7aWYoZz49cilyZXR1cm4gdGhpcy5pbnRlcmltWzBdPWksYTtpZigxMjghPSgxOTImKG49ZVtnKytdKSkpe2ctLTtjb250aW51ZX1pZihnPj1yKXJldHVybiB0aGlzLmludGVyaW1bMF09aSx0aGlzLmludGVyaW1bMV09bixhO2lmKDEyOCE9KDE5MiYobz1lW2crK10pKSl7Zy0tO2NvbnRpbnVlfWlmKGc+PXIpcmV0dXJuIHRoaXMuaW50ZXJpbVswXT1pLHRoaXMuaW50ZXJpbVsxXT1uLHRoaXMuaW50ZXJpbVsyXT1vLGE7aWYoMTI4IT0oMTkyJihzPWVbZysrXSkpKXtnLS07Y29udGludWV9aWYoKGM9KDcmaSk8PDE4fCg2MyZuKTw8MTJ8KDYzJm8pPDw2fDYzJnMpPDY1NTM2fHxjPjExMTQxMTEpY29udGludWU7dFthKytdPWN9fXJldHVybiBhfSxlfSgpO3QuVXRmOFRvVXRmMzI9aX0sMjI1OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Vbmljb2RlVjY9dm9pZCAwO3ZhciBpLG49cig4MjczKSxvPVtbNzY4LDg3OV0sWzExNTUsMTE1OF0sWzExNjAsMTE2MV0sWzE0MjUsMTQ2OV0sWzE0NzEsMTQ3MV0sWzE0NzMsMTQ3NF0sWzE0NzYsMTQ3N10sWzE0NzksMTQ3OV0sWzE1MzYsMTUzOV0sWzE1NTIsMTU1N10sWzE2MTEsMTYzMF0sWzE2NDgsMTY0OF0sWzE3NTAsMTc2NF0sWzE3NjcsMTc2OF0sWzE3NzAsMTc3M10sWzE4MDcsMTgwN10sWzE4MDksMTgwOV0sWzE4NDAsMTg2Nl0sWzE5NTgsMTk2OF0sWzIwMjcsMjAzNV0sWzIzMDUsMjMwNl0sWzIzNjQsMjM2NF0sWzIzNjksMjM3Nl0sWzIzODEsMjM4MV0sWzIzODUsMjM4OF0sWzI0MDIsMjQwM10sWzI0MzMsMjQzM10sWzI0OTIsMjQ5Ml0sWzI0OTcsMjUwMF0sWzI1MDksMjUwOV0sWzI1MzAsMjUzMV0sWzI1NjEsMjU2Ml0sWzI2MjAsMjYyMF0sWzI2MjUsMjYyNl0sWzI2MzEsMjYzMl0sWzI2MzUsMjYzN10sWzI2NzIsMjY3M10sWzI2ODksMjY5MF0sWzI3NDgsMjc0OF0sWzI3NTMsMjc1N10sWzI3NTksMjc2MF0sWzI3NjUsMjc2NV0sWzI3ODYsMjc4N10sWzI4MTcsMjgxN10sWzI4NzYsMjg3Nl0sWzI4NzksMjg3OV0sWzI4ODEsMjg4M10sWzI4OTMsMjg5M10sWzI5MDIsMjkwMl0sWzI5NDYsMjk0Nl0sWzMwMDgsMzAwOF0sWzMwMjEsMzAyMV0sWzMxMzQsMzEzNl0sWzMxNDIsMzE0NF0sWzMxNDYsMzE0OV0sWzMxNTcsMzE1OF0sWzMyNjAsMzI2MF0sWzMyNjMsMzI2M10sWzMyNzAsMzI3MF0sWzMyNzYsMzI3N10sWzMyOTgsMzI5OV0sWzMzOTMsMzM5NV0sWzM0MDUsMzQwNV0sWzM1MzAsMzUzMF0sWzM1MzgsMzU0MF0sWzM1NDIsMzU0Ml0sWzM2MzMsMzYzM10sWzM2MzYsMzY0Ml0sWzM2NTUsMzY2Ml0sWzM3NjEsMzc2MV0sWzM3NjQsMzc2OV0sWzM3NzEsMzc3Ml0sWzM3ODQsMzc4OV0sWzM4NjQsMzg2NV0sWzM4OTMsMzg5M10sWzM4OTUsMzg5NV0sWzM4OTcsMzg5N10sWzM5NTMsMzk2Nl0sWzM5NjgsMzk3Ml0sWzM5NzQsMzk3NV0sWzM5ODQsMzk5MV0sWzM5OTMsNDAyOF0sWzQwMzgsNDAzOF0sWzQxNDEsNDE0NF0sWzQxNDYsNDE0Nl0sWzQxNTAsNDE1MV0sWzQxNTMsNDE1M10sWzQxODQsNDE4NV0sWzQ0NDgsNDYwN10sWzQ5NTksNDk1OV0sWzU5MDYsNTkwOF0sWzU5MzgsNTk0MF0sWzU5NzAsNTk3MV0sWzYwMDIsNjAwM10sWzYwNjgsNjA2OV0sWzYwNzEsNjA3N10sWzYwODYsNjA4Nl0sWzYwODksNjA5OV0sWzYxMDksNjEwOV0sWzYxNTUsNjE1N10sWzYzMTMsNjMxM10sWzY0MzIsNjQzNF0sWzY0MzksNjQ0MF0sWzY0NTAsNjQ1MF0sWzY0NTcsNjQ1OV0sWzY2NzksNjY4MF0sWzY5MTIsNjkxNV0sWzY5NjQsNjk2NF0sWzY5NjYsNjk3MF0sWzY5NzIsNjk3Ml0sWzY5NzgsNjk3OF0sWzcwMTksNzAyN10sWzc2MTYsNzYyNl0sWzc2NzgsNzY3OV0sWzgyMDMsODIwN10sWzgyMzQsODIzOF0sWzgyODgsODI5MV0sWzgyOTgsODMwM10sWzg0MDAsODQzMV0sWzEyMzMwLDEyMzM1XSxbMTI0NDEsMTI0NDJdLFs0MzAxNCw0MzAxNF0sWzQzMDE5LDQzMDE5XSxbNDMwNDUsNDMwNDZdLFs2NDI4Niw2NDI4Nl0sWzY1MDI0LDY1MDM5XSxbNjUwNTYsNjUwNTldLFs2NTI3OSw2NTI3OV0sWzY1NTI5LDY1NTMxXV0scz1bWzY4MDk3LDY4MDk5XSxbNjgxMDEsNjgxMDJdLFs2ODEwOCw2ODExMV0sWzY4MTUyLDY4MTU0XSxbNjgxNTksNjgxNTldLFsxMTkxNDMsMTE5MTQ1XSxbMTE5MTU1LDExOTE3MF0sWzExOTE3MywxMTkxNzldLFsxMTkyMTAsMTE5MjEzXSxbMTE5MzYyLDExOTM2NF0sWzkxNzUwNSw5MTc1MDVdLFs5MTc1MzYsOTE3NjMxXSxbOTE3NzYwLDkxNzk5OV1dLGE9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKCl7aWYodGhpcy52ZXJzaW9uPSI2IiwhaSl7aT1uZXcgVWludDhBcnJheSg2NTUzNiksKDAsbi5maWxsKShpLDEpLGlbMF09MCwoMCxuLmZpbGwpKGksMCwxLDMyKSwoMCxuLmZpbGwpKGksMCwxMjcsMTYwKSwoMCxuLmZpbGwpKGksMiw0MzUyLDQ0NDgpLGlbOTAwMV09MixpWzkwMDJdPTIsKDAsbi5maWxsKShpLDIsMTE5MDQsNDIxOTIpLGlbMTIzNTFdPTEsKDAsbi5maWxsKShpLDIsNDQwMzIsNTUyMDQpLCgwLG4uZmlsbCkoaSwyLDYzNzQ0LDY0MjU2KSwoMCxuLmZpbGwpKGksMiw2NTA0MCw2NTA1MCksKDAsbi5maWxsKShpLDIsNjUwNzIsNjUxMzYpLCgwLG4uZmlsbCkoaSwyLDY1MjgwLDY1Mzc3KSwoMCxuLmZpbGwpKGksMiw2NTUwNCw2NTUxMSk7Zm9yKHZhciBlPTA7ZTxvLmxlbmd0aDsrK2UpKDAsbi5maWxsKShpLDAsb1tlXVswXSxvW2VdWzFdKzEpfX1yZXR1cm4gZS5wcm90b3R5cGUud2N3aWR0aD1mdW5jdGlvbihlKXtyZXR1cm4gZTwzMj8wOmU8MTI3PzE6ZTw2NTUzNj9pW2VdOmZ1bmN0aW9uKGUsdCl7dmFyIHIsaT0wLG49dC5sZW5ndGgtMTtpZihlPHRbMF1bMF18fGU+dFtuXVsxXSlyZXR1cm4hMTtmb3IoO24+PWk7KWlmKGU+dFtyPWkrbj4+MV1bMV0paT1yKzE7ZWxzZXtpZighKGU8dFtyXVswXSkpcmV0dXJuITA7bj1yLTF9cmV0dXJuITF9KGUscyk/MDplPj0xMzEwNzImJmU8PTE5NjYwNXx8ZT49MTk2NjA4JiZlPD0yNjIxNDE/MjoxfSxlfSgpO3QuVW5pY29kZVY2PWF9LDU5ODE6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Xcml0ZUJ1ZmZlcj12b2lkIDA7dmFyIHI9InVuZGVmaW5lZCI9PXR5cGVvZiBxdWV1ZU1pY3JvdGFzaz9mdW5jdGlvbihlKXtQcm9taXNlLnJlc29sdmUoKS50aGVuKGUpfTpxdWV1ZU1pY3JvdGFzayxpPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9hY3Rpb249ZSx0aGlzLl93cml0ZUJ1ZmZlcj1bXSx0aGlzLl9jYWxsYmFja3M9W10sdGhpcy5fcGVuZGluZ0RhdGE9MCx0aGlzLl9idWZmZXJPZmZzZXQ9MCx0aGlzLl9pc1N5bmNXcml0aW5nPSExLHRoaXMuX3N5bmNDYWxscz0wfXJldHVybiBlLnByb3RvdHlwZS53cml0ZVN5bmM9ZnVuY3Rpb24oZSx0KXtpZih2b2lkIDAhPT10JiZ0aGlzLl9zeW5jQ2FsbHM+dCl0aGlzLl9zeW5jQ2FsbHM9MDtlbHNlIGlmKHRoaXMuX3BlbmRpbmdEYXRhKz1lLmxlbmd0aCx0aGlzLl93cml0ZUJ1ZmZlci5wdXNoKGUpLHRoaXMuX2NhbGxiYWNrcy5wdXNoKHZvaWQgMCksdGhpcy5fc3luY0NhbGxzKyssIXRoaXMuX2lzU3luY1dyaXRpbmcpe3ZhciByO2Zvcih0aGlzLl9pc1N5bmNXcml0aW5nPSEwO3I9dGhpcy5fd3JpdGVCdWZmZXIuc2hpZnQoKTspe3RoaXMuX2FjdGlvbihyKTt2YXIgaT10aGlzLl9jYWxsYmFja3Muc2hpZnQoKTtpJiZpKCl9dGhpcy5fcGVuZGluZ0RhdGE9MCx0aGlzLl9idWZmZXJPZmZzZXQ9MjE0NzQ4MzY0Nyx0aGlzLl9pc1N5bmNXcml0aW5nPSExLHRoaXMuX3N5bmNDYWxscz0wfX0sZS5wcm90b3R5cGUud3JpdGU9ZnVuY3Rpb24oZSx0KXt2YXIgcj10aGlzO2lmKHRoaXMuX3BlbmRpbmdEYXRhPjVlNyl0aHJvdyBuZXcgRXJyb3IoIndyaXRlIGRhdGEgZGlzY2FyZGVkLCB1c2UgZmxvdyBjb250cm9sIHRvIGF2b2lkIGxvc2luZyBkYXRhIik7dGhpcy5fd3JpdGVCdWZmZXIubGVuZ3RofHwodGhpcy5fYnVmZmVyT2Zmc2V0PTAsc2V0VGltZW91dCgoZnVuY3Rpb24oKXtyZXR1cm4gci5faW5uZXJXcml0ZSgpfSkpKSx0aGlzLl9wZW5kaW5nRGF0YSs9ZS5sZW5ndGgsdGhpcy5fd3JpdGVCdWZmZXIucHVzaChlKSx0aGlzLl9jYWxsYmFja3MucHVzaCh0KX0sZS5wcm90b3R5cGUuX2lubmVyV3JpdGU9ZnVuY3Rpb24oZSx0KXt2YXIgaT10aGlzO3ZvaWQgMD09PWUmJihlPTApLHZvaWQgMD09PXQmJih0PSEwKTtmb3IodmFyIG49ZXx8RGF0ZS5ub3coKTt0aGlzLl93cml0ZUJ1ZmZlci5sZW5ndGg+dGhpcy5fYnVmZmVyT2Zmc2V0Oyl7dmFyIG89dGhpcy5fd3JpdGVCdWZmZXJbdGhpcy5fYnVmZmVyT2Zmc2V0XSxzPXRoaXMuX2FjdGlvbihvLHQpO2lmKHMpcmV0dXJuIHZvaWQgcy5jYXRjaCgoZnVuY3Rpb24oZSl7cmV0dXJuIHIoKGZ1bmN0aW9uKCl7dGhyb3cgZX0pKSxQcm9taXNlLnJlc29sdmUoITEpfSkpLnRoZW4oKGZ1bmN0aW9uKGUpe3JldHVybiBEYXRlLm5vdygpLW4+PTEyP3NldFRpbWVvdXQoKGZ1bmN0aW9uKCl7cmV0dXJuIGkuX2lubmVyV3JpdGUoMCxlKX0pKTppLl9pbm5lcldyaXRlKG4sZSl9KSk7dmFyIGE9dGhpcy5fY2FsbGJhY2tzW3RoaXMuX2J1ZmZlck9mZnNldF07aWYoYSYmYSgpLHRoaXMuX2J1ZmZlck9mZnNldCsrLHRoaXMuX3BlbmRpbmdEYXRhLT1vLmxlbmd0aCxEYXRlLm5vdygpLW4+PTEyKWJyZWFrfXRoaXMuX3dyaXRlQnVmZmVyLmxlbmd0aD50aGlzLl9idWZmZXJPZmZzZXQ/KHRoaXMuX2J1ZmZlck9mZnNldD41MCYmKHRoaXMuX3dyaXRlQnVmZmVyPXRoaXMuX3dyaXRlQnVmZmVyLnNsaWNlKHRoaXMuX2J1ZmZlck9mZnNldCksdGhpcy5fY2FsbGJhY2tzPXRoaXMuX2NhbGxiYWNrcy5zbGljZSh0aGlzLl9idWZmZXJPZmZzZXQpLHRoaXMuX2J1ZmZlck9mZnNldD0wKSxzZXRUaW1lb3V0KChmdW5jdGlvbigpe3JldHVybiBpLl9pbm5lcldyaXRlKCl9KSkpOih0aGlzLl93cml0ZUJ1ZmZlci5sZW5ndGg9MCx0aGlzLl9jYWxsYmFja3MubGVuZ3RoPTAsdGhpcy5fcGVuZGluZ0RhdGE9MCx0aGlzLl9idWZmZXJPZmZzZXQ9MCl9LGV9KCk7dC5Xcml0ZUJ1ZmZlcj1pfSw1OTQxOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQudG9SZ2JTdHJpbmc9dC5wYXJzZUNvbG9yPXZvaWQgMDt2YXIgcj0vXihbXGRhLWZdezF9KVwvKFtcZGEtZl17MX0pXC8oW1xkYS1mXXsxfSkkfF4oW1xkYS1mXXsyfSlcLyhbXGRhLWZdezJ9KVwvKFtcZGEtZl17Mn0pJHxeKFtcZGEtZl17M30pXC8oW1xkYS1mXXszfSlcLyhbXGRhLWZdezN9KSR8XihbXGRhLWZdezR9KVwvKFtcZGEtZl17NH0pXC8oW1xkYS1mXXs0fSkkLyxpPS9eW1xkYS1mXSskLztmdW5jdGlvbiBuKGUsdCl7dmFyIHI9ZS50b1N0cmluZygxNiksaT1yLmxlbmd0aDwyPyIwIityOnI7c3dpdGNoKHQpe2Nhc2UgNDpyZXR1cm4gclswXTtjYXNlIDg6cmV0dXJuIGk7Y2FzZSAxMjpyZXR1cm4oaStpKS5zbGljZSgwLDMpO2RlZmF1bHQ6cmV0dXJuIGkraX19dC5wYXJzZUNvbG9yPWZ1bmN0aW9uKGUpe2lmKGUpe3ZhciB0PWUudG9Mb3dlckNhc2UoKTtpZigwPT09dC5pbmRleE9mKCJyZ2I6Iikpe3Q9dC5zbGljZSg0KTt2YXIgbj1yLmV4ZWModCk7aWYobil7dmFyIG89blsxXT8xNTpuWzRdPzI1NTpuWzddPzQwOTU6NjU1MzU7cmV0dXJuW01hdGgucm91bmQocGFyc2VJbnQoblsxXXx8bls0XXx8bls3XXx8blsxMF0sMTYpL28qMjU1KSxNYXRoLnJvdW5kKHBhcnNlSW50KG5bMl18fG5bNV18fG5bOF18fG5bMTFdLDE2KS9vKjI1NSksTWF0aC5yb3VuZChwYXJzZUludChuWzNdfHxuWzZdfHxuWzldfHxuWzEyXSwxNikvbyoyNTUpXX19ZWxzZSBpZigwPT09dC5pbmRleE9mKCIjIikmJih0PXQuc2xpY2UoMSksaS5leGVjKHQpJiZbMyw2LDksMTJdLmluY2x1ZGVzKHQubGVuZ3RoKSkpe2Zvcih2YXIgcz10Lmxlbmd0aC8zLGE9WzAsMCwwXSxjPTA7YzwzOysrYyl7dmFyIGw9cGFyc2VJbnQodC5zbGljZShzKmMscypjK3MpLDE2KTthW2NdPTE9PT1zP2w8PDQ6Mj09PXM/bDozPT09cz9sPj40Omw+Pjh9cmV0dXJuIGF9fX0sdC50b1JnYlN0cmluZz1mdW5jdGlvbihlLHQpe3ZvaWQgMD09PXQmJih0PTE2KTt2YXIgcj1lWzBdLGk9ZVsxXSxvPWVbMl07cmV0dXJuInJnYjoiK24ocix0KSsiLyIrbihpLHQpKyIvIituKG8sdCl9fSw1NzcwOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuUEFZTE9BRF9MSU1JVD12b2lkIDAsdC5QQVlMT0FEX0xJTUlUPTFlN30sNjM1MTooZSx0LHIpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuRGNzSGFuZGxlcj10LkRjc1BhcnNlcj12b2lkIDA7dmFyIGk9cig0ODIpLG49cig4NzQyKSxvPXIoNTc3MCkscz1bXSxhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX2hhbmRsZXJzPU9iamVjdC5jcmVhdGUobnVsbCksdGhpcy5fYWN0aXZlPXMsdGhpcy5faWRlbnQ9MCx0aGlzLl9oYW5kbGVyRmI9ZnVuY3Rpb24oKXt9LHRoaXMuX3N0YWNrPXtwYXVzZWQ6ITEsbG9vcFBvc2l0aW9uOjAsZmFsbFRocm91Z2g6ITF9fXJldHVybiBlLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5faGFuZGxlcnM9T2JqZWN0LmNyZWF0ZShudWxsKSx0aGlzLl9oYW5kbGVyRmI9ZnVuY3Rpb24oKXt9LHRoaXMuX2FjdGl2ZT1zfSxlLnByb3RvdHlwZS5yZWdpc3RlckhhbmRsZXI9ZnVuY3Rpb24oZSx0KXt2b2lkIDA9PT10aGlzLl9oYW5kbGVyc1tlXSYmKHRoaXMuX2hhbmRsZXJzW2VdPVtdKTt2YXIgcj10aGlzLl9oYW5kbGVyc1tlXTtyZXR1cm4gci5wdXNoKHQpLHtkaXNwb3NlOmZ1bmN0aW9uKCl7dmFyIGU9ci5pbmRleE9mKHQpOy0xIT09ZSYmci5zcGxpY2UoZSwxKX19fSxlLnByb3RvdHlwZS5jbGVhckhhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5faGFuZGxlcnNbZV0mJmRlbGV0ZSB0aGlzLl9oYW5kbGVyc1tlXX0sZS5wcm90b3R5cGUuc2V0SGFuZGxlckZhbGxiYWNrPWZ1bmN0aW9uKGUpe3RoaXMuX2hhbmRsZXJGYj1lfSxlLnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe2lmKHRoaXMuX2FjdGl2ZS5sZW5ndGgpZm9yKHZhciBlPXRoaXMuX3N0YWNrLnBhdXNlZD90aGlzLl9zdGFjay5sb29wUG9zaXRpb24tMTp0aGlzLl9hY3RpdmUubGVuZ3RoLTE7ZT49MDstLWUpdGhpcy5fYWN0aXZlW2VdLnVuaG9vayghMSk7dGhpcy5fc3RhY2sucGF1c2VkPSExLHRoaXMuX2FjdGl2ZT1zLHRoaXMuX2lkZW50PTB9LGUucHJvdG90eXBlLmhvb2s9ZnVuY3Rpb24oZSx0KXtpZih0aGlzLnJlc2V0KCksdGhpcy5faWRlbnQ9ZSx0aGlzLl9hY3RpdmU9dGhpcy5faGFuZGxlcnNbZV18fHMsdGhpcy5fYWN0aXZlLmxlbmd0aClmb3IodmFyIHI9dGhpcy5fYWN0aXZlLmxlbmd0aC0xO3I+PTA7ci0tKXRoaXMuX2FjdGl2ZVtyXS5ob29rKHQpO2Vsc2UgdGhpcy5faGFuZGxlckZiKHRoaXMuX2lkZW50LCJIT09LIix0KX0sZS5wcm90b3R5cGUucHV0PWZ1bmN0aW9uKGUsdCxyKXtpZih0aGlzLl9hY3RpdmUubGVuZ3RoKWZvcih2YXIgbj10aGlzLl9hY3RpdmUubGVuZ3RoLTE7bj49MDtuLS0pdGhpcy5fYWN0aXZlW25dLnB1dChlLHQscik7ZWxzZSB0aGlzLl9oYW5kbGVyRmIodGhpcy5faWRlbnQsIlBVVCIsKDAsaS51dGYzMlRvU3RyaW5nKShlLHQscikpfSxlLnByb3RvdHlwZS51bmhvb2s9ZnVuY3Rpb24oZSx0KXtpZih2b2lkIDA9PT10JiYodD0hMCksdGhpcy5fYWN0aXZlLmxlbmd0aCl7dmFyIHI9ITEsaT10aGlzLl9hY3RpdmUubGVuZ3RoLTEsbj0hMTtpZih0aGlzLl9zdGFjay5wYXVzZWQmJihpPXRoaXMuX3N0YWNrLmxvb3BQb3NpdGlvbi0xLHI9dCxuPXRoaXMuX3N0YWNrLmZhbGxUaHJvdWdoLHRoaXMuX3N0YWNrLnBhdXNlZD0hMSksIW4mJiExPT09cil7Zm9yKDtpPj0wJiYhMCE9PShyPXRoaXMuX2FjdGl2ZVtpXS51bmhvb2soZSkpO2ktLSlpZihyIGluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gdGhpcy5fc3RhY2sucGF1c2VkPSEwLHRoaXMuX3N0YWNrLmxvb3BQb3NpdGlvbj1pLHRoaXMuX3N0YWNrLmZhbGxUaHJvdWdoPSExLHI7aS0tfWZvcig7aT49MDtpLS0paWYoKHI9dGhpcy5fYWN0aXZlW2ldLnVuaG9vayghMSkpaW5zdGFuY2VvZiBQcm9taXNlKXJldHVybiB0aGlzLl9zdGFjay5wYXVzZWQ9ITAsdGhpcy5fc3RhY2subG9vcFBvc2l0aW9uPWksdGhpcy5fc3RhY2suZmFsbFRocm91Z2g9ITAscn1lbHNlIHRoaXMuX2hhbmRsZXJGYih0aGlzLl9pZGVudCwiVU5IT09LIixlKTt0aGlzLl9hY3RpdmU9cyx0aGlzLl9pZGVudD0wfSxlfSgpO3QuRGNzUGFyc2VyPWE7dmFyIGM9bmV3IG4uUGFyYW1zO2MuYWRkUGFyYW0oMCk7dmFyIGw9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe3RoaXMuX2hhbmRsZXI9ZSx0aGlzLl9kYXRhPSIiLHRoaXMuX3BhcmFtcz1jLHRoaXMuX2hpdExpbWl0PSExfXJldHVybiBlLnByb3RvdHlwZS5ob29rPWZ1bmN0aW9uKGUpe3RoaXMuX3BhcmFtcz1lLmxlbmd0aD4xfHxlLnBhcmFtc1swXT9lLmNsb25lKCk6Yyx0aGlzLl9kYXRhPSIiLHRoaXMuX2hpdExpbWl0PSExfSxlLnByb3RvdHlwZS5wdXQ9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2hpdExpbWl0fHwodGhpcy5fZGF0YSs9KDAsaS51dGYzMlRvU3RyaW5nKShlLHQsciksdGhpcy5fZGF0YS5sZW5ndGg+by5QQVlMT0FEX0xJTUlUJiYodGhpcy5fZGF0YT0iIix0aGlzLl9oaXRMaW1pdD0hMCkpfSxlLnByb3RvdHlwZS51bmhvb2s9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxyPSExO2lmKHRoaXMuX2hpdExpbWl0KXI9ITE7ZWxzZSBpZihlJiYocj10aGlzLl9oYW5kbGVyKHRoaXMuX2RhdGEsdGhpcy5fcGFyYW1zKSlpbnN0YW5jZW9mIFByb21pc2UpcmV0dXJuIHIudGhlbigoZnVuY3Rpb24oZSl7cmV0dXJuIHQuX3BhcmFtcz1jLHQuX2RhdGE9IiIsdC5faGl0TGltaXQ9ITEsZX0pKTtyZXR1cm4gdGhpcy5fcGFyYW1zPWMsdGhpcy5fZGF0YT0iIix0aGlzLl9oaXRMaW1pdD0hMSxyfSxlfSgpO3QuRGNzSGFuZGxlcj1sfSwyMDE1OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pO09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkVzY2FwZVNlcXVlbmNlUGFyc2VyPXQuVlQ1MDBfVFJBTlNJVElPTl9UQUJMRT10LlRyYW5zaXRpb25UYWJsZT12b2lkIDA7dmFyIG89cig4NDQpLHM9cig4MjczKSxhPXIoODc0MiksYz1yKDYyNDIpLGw9cig2MzUxKSx1PWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLnRhYmxlPW5ldyBVaW50OEFycmF5KGUpfXJldHVybiBlLnByb3RvdHlwZS5zZXREZWZhdWx0PWZ1bmN0aW9uKGUsdCl7KDAscy5maWxsKSh0aGlzLnRhYmxlLGU8PDR8dCl9LGUucHJvdG90eXBlLmFkZD1mdW5jdGlvbihlLHQscixpKXt0aGlzLnRhYmxlW3Q8PDh8ZV09cjw8NHxpfSxlLnByb3RvdHlwZS5hZGRNYW55PWZ1bmN0aW9uKGUsdCxyLGkpe2Zvcih2YXIgbj0wO248ZS5sZW5ndGg7bisrKXRoaXMudGFibGVbdDw8OHxlW25dXT1yPDw0fGl9LGV9KCk7dC5UcmFuc2l0aW9uVGFibGU9dTt2YXIgaD0xNjA7dC5WVDUwMF9UUkFOU0lUSU9OX1RBQkxFPWZ1bmN0aW9uKCl7dmFyIGU9bmV3IHUoNDA5NSksdD1BcnJheS5hcHBseShudWxsLEFycmF5KDI1NikpLm1hcCgoZnVuY3Rpb24oZSx0KXtyZXR1cm4gdH0pKSxyPWZ1bmN0aW9uKGUscil7cmV0dXJuIHQuc2xpY2UoZSxyKX0saT1yKDMyLDEyNyksbj1yKDAsMjQpO24ucHVzaCgyNSksbi5wdXNoLmFwcGx5KG4scigyOCwzMikpO3ZhciBvLHM9cigwLDE0KTtmb3IobyBpbiBlLnNldERlZmF1bHQoMSwwKSxlLmFkZE1hbnkoaSwwLDIsMCkscyllLmFkZE1hbnkoWzI0LDI2LDE1MywxNTRdLG8sMywwKSxlLmFkZE1hbnkocigxMjgsMTQ0KSxvLDMsMCksZS5hZGRNYW55KHIoMTQ0LDE1MiksbywzLDApLGUuYWRkKDE1NixvLDAsMCksZS5hZGQoMjcsbywxMSwxKSxlLmFkZCgxNTcsbyw0LDgpLGUuYWRkTWFueShbMTUyLDE1OCwxNTldLG8sMCw3KSxlLmFkZCgxNTUsbywxMSwzKSxlLmFkZCgxNDQsbywxMSw5KTtyZXR1cm4gZS5hZGRNYW55KG4sMCwzLDApLGUuYWRkTWFueShuLDEsMywxKSxlLmFkZCgxMjcsMSwwLDEpLGUuYWRkTWFueShuLDgsMCw4KSxlLmFkZE1hbnkobiwzLDMsMyksZS5hZGQoMTI3LDMsMCwzKSxlLmFkZE1hbnkobiw0LDMsNCksZS5hZGQoMTI3LDQsMCw0KSxlLmFkZE1hbnkobiw2LDMsNiksZS5hZGRNYW55KG4sNSwzLDUpLGUuYWRkKDEyNyw1LDAsNSksZS5hZGRNYW55KG4sMiwzLDIpLGUuYWRkKDEyNywyLDAsMiksZS5hZGQoOTMsMSw0LDgpLGUuYWRkTWFueShpLDgsNSw4KSxlLmFkZCgxMjcsOCw1LDgpLGUuYWRkTWFueShbMTU2LDI3LDI0LDI2LDddLDgsNiwwKSxlLmFkZE1hbnkocigyOCwzMiksOCwwLDgpLGUuYWRkTWFueShbODgsOTQsOTVdLDEsMCw3KSxlLmFkZE1hbnkoaSw3LDAsNyksZS5hZGRNYW55KG4sNywwLDcpLGUuYWRkKDE1Niw3LDAsMCksZS5hZGQoMTI3LDcsMCw3KSxlLmFkZCg5MSwxLDExLDMpLGUuYWRkTWFueShyKDY0LDEyNyksMyw3LDApLGUuYWRkTWFueShyKDQ4LDYwKSwzLDgsNCksZS5hZGRNYW55KFs2MCw2MSw2Miw2M10sMyw5LDQpLGUuYWRkTWFueShyKDQ4LDYwKSw0LDgsNCksZS5hZGRNYW55KHIoNjQsMTI3KSw0LDcsMCksZS5hZGRNYW55KFs2MCw2MSw2Miw2M10sNCwwLDYpLGUuYWRkTWFueShyKDMyLDY0KSw2LDAsNiksZS5hZGQoMTI3LDYsMCw2KSxlLmFkZE1hbnkocig2NCwxMjcpLDYsMCwwKSxlLmFkZE1hbnkocigzMiw0OCksMyw5LDUpLGUuYWRkTWFueShyKDMyLDQ4KSw1LDksNSksZS5hZGRNYW55KHIoNDgsNjQpLDUsMCw2KSxlLmFkZE1hbnkocig2NCwxMjcpLDUsNywwKSxlLmFkZE1hbnkocigzMiw0OCksNCw5LDUpLGUuYWRkTWFueShyKDMyLDQ4KSwxLDksMiksZS5hZGRNYW55KHIoMzIsNDgpLDIsOSwyKSxlLmFkZE1hbnkocig0OCwxMjcpLDIsMTAsMCksZS5hZGRNYW55KHIoNDgsODApLDEsMTAsMCksZS5hZGRNYW55KHIoODEsODgpLDEsMTAsMCksZS5hZGRNYW55KFs4OSw5MCw5Ml0sMSwxMCwwKSxlLmFkZE1hbnkocig5NiwxMjcpLDEsMTAsMCksZS5hZGQoODAsMSwxMSw5KSxlLmFkZE1hbnkobiw5LDAsOSksZS5hZGQoMTI3LDksMCw5KSxlLmFkZE1hbnkocigyOCwzMiksOSwwLDkpLGUuYWRkTWFueShyKDMyLDQ4KSw5LDksMTIpLGUuYWRkTWFueShyKDQ4LDYwKSw5LDgsMTApLGUuYWRkTWFueShbNjAsNjEsNjIsNjNdLDksOSwxMCksZS5hZGRNYW55KG4sMTEsMCwxMSksZS5hZGRNYW55KHIoMzIsMTI4KSwxMSwwLDExKSxlLmFkZE1hbnkocigyOCwzMiksMTEsMCwxMSksZS5hZGRNYW55KG4sMTAsMCwxMCksZS5hZGQoMTI3LDEwLDAsMTApLGUuYWRkTWFueShyKDI4LDMyKSwxMCwwLDEwKSxlLmFkZE1hbnkocig0OCw2MCksMTAsOCwxMCksZS5hZGRNYW55KFs2MCw2MSw2Miw2M10sMTAsMCwxMSksZS5hZGRNYW55KHIoMzIsNDgpLDEwLDksMTIpLGUuYWRkTWFueShuLDEyLDAsMTIpLGUuYWRkKDEyNywxMiwwLDEyKSxlLmFkZE1hbnkocigyOCwzMiksMTIsMCwxMiksZS5hZGRNYW55KHIoMzIsNDgpLDEyLDksMTIpLGUuYWRkTWFueShyKDQ4LDY0KSwxMiwwLDExKSxlLmFkZE1hbnkocig2NCwxMjcpLDEyLDEyLDEzKSxlLmFkZE1hbnkocig2NCwxMjcpLDEwLDEyLDEzKSxlLmFkZE1hbnkocig2NCwxMjcpLDksMTIsMTMpLGUuYWRkTWFueShuLDEzLDEzLDEzKSxlLmFkZE1hbnkoaSwxMywxMywxMyksZS5hZGQoMTI3LDEzLDAsMTMpLGUuYWRkTWFueShbMjcsMTU2LDI0LDI2XSwxMywxNCwwKSxlLmFkZChoLDAsMiwwKSxlLmFkZChoLDgsNSw4KSxlLmFkZChoLDYsMCw2KSxlLmFkZChoLDExLDAsMTEpLGUuYWRkKGgsMTMsMTMsMTMpLGV9KCk7dmFyIGY9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gcihyKXt2b2lkIDA9PT1yJiYocj10LlZUNTAwX1RSQU5TSVRJT05fVEFCTEUpO3ZhciBpPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gaS5fdHJhbnNpdGlvbnM9cixpLl9wYXJzZVN0YWNrPXtzdGF0ZTowLGhhbmRsZXJzOltdLGhhbmRsZXJQb3M6MCx0cmFuc2l0aW9uOjAsY2h1bmtQb3M6MH0saS5pbml0aWFsU3RhdGU9MCxpLmN1cnJlbnRTdGF0ZT1pLmluaXRpYWxTdGF0ZSxpLl9wYXJhbXM9bmV3IGEuUGFyYW1zLGkuX3BhcmFtcy5hZGRQYXJhbSgwKSxpLl9jb2xsZWN0PTAsaS5wcmVjZWRpbmdDb2RlcG9pbnQ9MCxpLl9wcmludEhhbmRsZXJGYj1mdW5jdGlvbihlLHQscil7fSxpLl9leGVjdXRlSGFuZGxlckZiPWZ1bmN0aW9uKGUpe30saS5fY3NpSGFuZGxlckZiPWZ1bmN0aW9uKGUsdCl7fSxpLl9lc2NIYW5kbGVyRmI9ZnVuY3Rpb24oZSl7fSxpLl9lcnJvckhhbmRsZXJGYj1mdW5jdGlvbihlKXtyZXR1cm4gZX0saS5fcHJpbnRIYW5kbGVyPWkuX3ByaW50SGFuZGxlckZiLGkuX2V4ZWN1dGVIYW5kbGVycz1PYmplY3QuY3JlYXRlKG51bGwpLGkuX2NzaUhhbmRsZXJzPU9iamVjdC5jcmVhdGUobnVsbCksaS5fZXNjSGFuZGxlcnM9T2JqZWN0LmNyZWF0ZShudWxsKSxpLl9vc2NQYXJzZXI9bmV3IGMuT3NjUGFyc2VyLGkuX2Rjc1BhcnNlcj1uZXcgbC5EY3NQYXJzZXIsaS5fZXJyb3JIYW5kbGVyPWkuX2Vycm9ySGFuZGxlckZiLGkucmVnaXN0ZXJFc2NIYW5kbGVyKHtmaW5hbDoiXFwifSwoZnVuY3Rpb24oKXtyZXR1cm4hMH0pKSxpfXJldHVybiBuKHIsZSksci5wcm90b3R5cGUuX2lkZW50aWZpZXI9ZnVuY3Rpb24oZSx0KXt2b2lkIDA9PT10JiYodD1bNjQsMTI2XSk7dmFyIHI9MDtpZihlLnByZWZpeCl7aWYoZS5wcmVmaXgubGVuZ3RoPjEpdGhyb3cgbmV3IEVycm9yKCJvbmx5IG9uZSBieXRlIGFzIHByZWZpeCBzdXBwb3J0ZWQiKTtpZigocj1lLnByZWZpeC5jaGFyQ29kZUF0KDApKSYmNjA+cnx8cj42Myl0aHJvdyBuZXcgRXJyb3IoInByZWZpeCBtdXN0IGJlIGluIHJhbmdlIDB4M2MgLi4gMHgzZiIpfWlmKGUuaW50ZXJtZWRpYXRlcyl7aWYoZS5pbnRlcm1lZGlhdGVzLmxlbmd0aD4yKXRocm93IG5ldyBFcnJvcigib25seSB0d28gYnl0ZXMgYXMgaW50ZXJtZWRpYXRlcyBhcmUgc3VwcG9ydGVkIik7Zm9yKHZhciBpPTA7aTxlLmludGVybWVkaWF0ZXMubGVuZ3RoOysraSl7dmFyIG49ZS5pbnRlcm1lZGlhdGVzLmNoYXJDb2RlQXQoaSk7aWYoMzI+bnx8bj40Nyl0aHJvdyBuZXcgRXJyb3IoImludGVybWVkaWF0ZSBtdXN0IGJlIGluIHJhbmdlIDB4MjAgLi4gMHgyZiIpO3I8PD04LHJ8PW59fWlmKDEhPT1lLmZpbmFsLmxlbmd0aCl0aHJvdyBuZXcgRXJyb3IoImZpbmFsIG11c3QgYmUgYSBzaW5nbGUgYnl0ZSIpO3ZhciBvPWUuZmluYWwuY2hhckNvZGVBdCgwKTtpZih0WzBdPm98fG8+dFsxXSl0aHJvdyBuZXcgRXJyb3IoImZpbmFsIG11c3QgYmUgaW4gcmFuZ2UgIit0WzBdKyIgLi4gIit0WzFdKTtyZXR1cm4ocjw8PTgpfG99LHIucHJvdG90eXBlLmlkZW50VG9TdHJpbmc9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PVtdO2U7KXQucHVzaChTdHJpbmcuZnJvbUNoYXJDb2RlKDI1NSZlKSksZT4+PTg7cmV0dXJuIHQucmV2ZXJzZSgpLmpvaW4oIiIpfSxyLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5fY3NpSGFuZGxlcnM9T2JqZWN0LmNyZWF0ZShudWxsKSx0aGlzLl9leGVjdXRlSGFuZGxlcnM9T2JqZWN0LmNyZWF0ZShudWxsKSx0aGlzLl9lc2NIYW5kbGVycz1PYmplY3QuY3JlYXRlKG51bGwpLHRoaXMuX29zY1BhcnNlci5kaXNwb3NlKCksdGhpcy5fZGNzUGFyc2VyLmRpc3Bvc2UoKX0sci5wcm90b3R5cGUuc2V0UHJpbnRIYW5kbGVyPWZ1bmN0aW9uKGUpe3RoaXMuX3ByaW50SGFuZGxlcj1lfSxyLnByb3RvdHlwZS5jbGVhclByaW50SGFuZGxlcj1mdW5jdGlvbigpe3RoaXMuX3ByaW50SGFuZGxlcj10aGlzLl9wcmludEhhbmRsZXJGYn0sci5wcm90b3R5cGUucmVnaXN0ZXJFc2NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7dmFyIHI9dGhpcy5faWRlbnRpZmllcihlLFs0OCwxMjZdKTt2b2lkIDA9PT10aGlzLl9lc2NIYW5kbGVyc1tyXSYmKHRoaXMuX2VzY0hhbmRsZXJzW3JdPVtdKTt2YXIgaT10aGlzLl9lc2NIYW5kbGVyc1tyXTtyZXR1cm4gaS5wdXNoKHQpLHtkaXNwb3NlOmZ1bmN0aW9uKCl7dmFyIGU9aS5pbmRleE9mKHQpOy0xIT09ZSYmaS5zcGxpY2UoZSwxKX19fSxyLnByb3RvdHlwZS5jbGVhckVzY0hhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5fZXNjSGFuZGxlcnNbdGhpcy5faWRlbnRpZmllcihlLFs0OCwxMjZdKV0mJmRlbGV0ZSB0aGlzLl9lc2NIYW5kbGVyc1t0aGlzLl9pZGVudGlmaWVyKGUsWzQ4LDEyNl0pXX0sci5wcm90b3R5cGUuc2V0RXNjSGFuZGxlckZhbGxiYWNrPWZ1bmN0aW9uKGUpe3RoaXMuX2VzY0hhbmRsZXJGYj1lfSxyLnByb3RvdHlwZS5zZXRFeGVjdXRlSGFuZGxlcj1mdW5jdGlvbihlLHQpe3RoaXMuX2V4ZWN1dGVIYW5kbGVyc1tlLmNoYXJDb2RlQXQoMCldPXR9LHIucHJvdG90eXBlLmNsZWFyRXhlY3V0ZUhhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5fZXhlY3V0ZUhhbmRsZXJzW2UuY2hhckNvZGVBdCgwKV0mJmRlbGV0ZSB0aGlzLl9leGVjdXRlSGFuZGxlcnNbZS5jaGFyQ29kZUF0KDApXX0sci5wcm90b3R5cGUuc2V0RXhlY3V0ZUhhbmRsZXJGYWxsYmFjaz1mdW5jdGlvbihlKXt0aGlzLl9leGVjdXRlSGFuZGxlckZiPWV9LHIucHJvdG90eXBlLnJlZ2lzdGVyQ3NpSGFuZGxlcj1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX2lkZW50aWZpZXIoZSk7dm9pZCAwPT09dGhpcy5fY3NpSGFuZGxlcnNbcl0mJih0aGlzLl9jc2lIYW5kbGVyc1tyXT1bXSk7dmFyIGk9dGhpcy5fY3NpSGFuZGxlcnNbcl07cmV0dXJuIGkucHVzaCh0KSx7ZGlzcG9zZTpmdW5jdGlvbigpe3ZhciBlPWkuaW5kZXhPZih0KTstMSE9PWUmJmkuc3BsaWNlKGUsMSl9fX0sci5wcm90b3R5cGUuY2xlYXJDc2lIYW5kbGVyPWZ1bmN0aW9uKGUpe3RoaXMuX2NzaUhhbmRsZXJzW3RoaXMuX2lkZW50aWZpZXIoZSldJiZkZWxldGUgdGhpcy5fY3NpSGFuZGxlcnNbdGhpcy5faWRlbnRpZmllcihlKV19LHIucHJvdG90eXBlLnNldENzaUhhbmRsZXJGYWxsYmFjaz1mdW5jdGlvbihlKXt0aGlzLl9jc2lIYW5kbGVyRmI9ZX0sci5wcm90b3R5cGUucmVnaXN0ZXJEY3NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMuX2Rjc1BhcnNlci5yZWdpc3RlckhhbmRsZXIodGhpcy5faWRlbnRpZmllcihlKSx0KX0sci5wcm90b3R5cGUuY2xlYXJEY3NIYW5kbGVyPWZ1bmN0aW9uKGUpe3RoaXMuX2Rjc1BhcnNlci5jbGVhckhhbmRsZXIodGhpcy5faWRlbnRpZmllcihlKSl9LHIucHJvdG90eXBlLnNldERjc0hhbmRsZXJGYWxsYmFjaz1mdW5jdGlvbihlKXt0aGlzLl9kY3NQYXJzZXIuc2V0SGFuZGxlckZhbGxiYWNrKGUpfSxyLnByb3RvdHlwZS5yZWdpc3Rlck9zY0hhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5fb3NjUGFyc2VyLnJlZ2lzdGVySGFuZGxlcihlLHQpfSxyLnByb3RvdHlwZS5jbGVhck9zY0hhbmRsZXI9ZnVuY3Rpb24oZSl7dGhpcy5fb3NjUGFyc2VyLmNsZWFySGFuZGxlcihlKX0sci5wcm90b3R5cGUuc2V0T3NjSGFuZGxlckZhbGxiYWNrPWZ1bmN0aW9uKGUpe3RoaXMuX29zY1BhcnNlci5zZXRIYW5kbGVyRmFsbGJhY2soZSl9LHIucHJvdG90eXBlLnNldEVycm9ySGFuZGxlcj1mdW5jdGlvbihlKXt0aGlzLl9lcnJvckhhbmRsZXI9ZX0sci5wcm90b3R5cGUuY2xlYXJFcnJvckhhbmRsZXI9ZnVuY3Rpb24oKXt0aGlzLl9lcnJvckhhbmRsZXI9dGhpcy5fZXJyb3JIYW5kbGVyRmJ9LHIucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5jdXJyZW50U3RhdGU9dGhpcy5pbml0aWFsU3RhdGUsdGhpcy5fb3NjUGFyc2VyLnJlc2V0KCksdGhpcy5fZGNzUGFyc2VyLnJlc2V0KCksdGhpcy5fcGFyYW1zLnJlc2V0KCksdGhpcy5fcGFyYW1zLmFkZFBhcmFtKDApLHRoaXMuX2NvbGxlY3Q9MCx0aGlzLnByZWNlZGluZ0NvZGVwb2ludD0wLDAhPT10aGlzLl9wYXJzZVN0YWNrLnN0YXRlJiYodGhpcy5fcGFyc2VTdGFjay5zdGF0ZT0yLHRoaXMuX3BhcnNlU3RhY2suaGFuZGxlcnM9W10pfSxyLnByb3RvdHlwZS5fcHJlc2VydmVTdGFjaz1mdW5jdGlvbihlLHQscixpLG4pe3RoaXMuX3BhcnNlU3RhY2suc3RhdGU9ZSx0aGlzLl9wYXJzZVN0YWNrLmhhbmRsZXJzPXQsdGhpcy5fcGFyc2VTdGFjay5oYW5kbGVyUG9zPXIsdGhpcy5fcGFyc2VTdGFjay50cmFuc2l0aW9uPWksdGhpcy5fcGFyc2VTdGFjay5jaHVua1Bvcz1ufSxyLnByb3RvdHlwZS5wYXJzZT1mdW5jdGlvbihlLHQscil7dmFyIGksbj0wLG89MCxzPTA7aWYodGhpcy5fcGFyc2VTdGFjay5zdGF0ZSlpZigyPT09dGhpcy5fcGFyc2VTdGFjay5zdGF0ZSl0aGlzLl9wYXJzZVN0YWNrLnN0YXRlPTAscz10aGlzLl9wYXJzZVN0YWNrLmNodW5rUG9zKzE7ZWxzZXtpZih2b2lkIDA9PT1yfHwxPT09dGhpcy5fcGFyc2VTdGFjay5zdGF0ZSl0aHJvdyB0aGlzLl9wYXJzZVN0YWNrLnN0YXRlPTEsbmV3IEVycm9yKCJpbXByb3BlciBjb250aW51YXRpb24gZHVlIHRvIHByZXZpb3VzIGFzeW5jIGhhbmRsZXIsIGdpdmluZyB1cCBwYXJzaW5nIik7dmFyIGE9dGhpcy5fcGFyc2VTdGFjay5oYW5kbGVycyxjPXRoaXMuX3BhcnNlU3RhY2suaGFuZGxlclBvcy0xO3N3aXRjaCh0aGlzLl9wYXJzZVN0YWNrLnN0YXRlKXtjYXNlIDM6aWYoITE9PT1yJiZjPi0xKWZvcig7Yz49MCYmITAhPT0oaT1hW2NdKHRoaXMuX3BhcmFtcykpO2MtLSlpZihpIGluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gdGhpcy5fcGFyc2VTdGFjay5oYW5kbGVyUG9zPWMsaTt0aGlzLl9wYXJzZVN0YWNrLmhhbmRsZXJzPVtdO2JyZWFrO2Nhc2UgNDppZighMT09PXImJmM+LTEpZm9yKDtjPj0wJiYhMCE9PShpPWFbY10oKSk7Yy0tKWlmKGkgaW5zdGFuY2VvZiBQcm9taXNlKXJldHVybiB0aGlzLl9wYXJzZVN0YWNrLmhhbmRsZXJQb3M9YyxpO3RoaXMuX3BhcnNlU3RhY2suaGFuZGxlcnM9W107YnJlYWs7Y2FzZSA2OmlmKG49ZVt0aGlzLl9wYXJzZVN0YWNrLmNodW5rUG9zXSxpPXRoaXMuX2Rjc1BhcnNlci51bmhvb2soMjQhPT1uJiYyNiE9PW4scikpcmV0dXJuIGk7Mjc9PT1uJiYodGhpcy5fcGFyc2VTdGFjay50cmFuc2l0aW9ufD0xKSx0aGlzLl9wYXJhbXMucmVzZXQoKSx0aGlzLl9wYXJhbXMuYWRkUGFyYW0oMCksdGhpcy5fY29sbGVjdD0wO2JyZWFrO2Nhc2UgNTppZihuPWVbdGhpcy5fcGFyc2VTdGFjay5jaHVua1Bvc10saT10aGlzLl9vc2NQYXJzZXIuZW5kKDI0IT09biYmMjYhPT1uLHIpKXJldHVybiBpOzI3PT09biYmKHRoaXMuX3BhcnNlU3RhY2sudHJhbnNpdGlvbnw9MSksdGhpcy5fcGFyYW1zLnJlc2V0KCksdGhpcy5fcGFyYW1zLmFkZFBhcmFtKDApLHRoaXMuX2NvbGxlY3Q9MH10aGlzLl9wYXJzZVN0YWNrLnN0YXRlPTAscz10aGlzLl9wYXJzZVN0YWNrLmNodW5rUG9zKzEsdGhpcy5wcmVjZWRpbmdDb2RlcG9pbnQ9MCx0aGlzLmN1cnJlbnRTdGF0ZT0xNSZ0aGlzLl9wYXJzZVN0YWNrLnRyYW5zaXRpb259Zm9yKHZhciBsPXM7bDx0OysrbCl7c3dpdGNoKG49ZVtsXSwobz10aGlzLl90cmFuc2l0aW9ucy50YWJsZVt0aGlzLmN1cnJlbnRTdGF0ZTw8OHwobjwxNjA/bjpoKV0pPj40KXtjYXNlIDI6Zm9yKHZhciB1PWwrMTs7Kyt1KXtpZih1Pj10fHwobj1lW3VdKTwzMnx8bj4xMjYmJm48aCl7dGhpcy5fcHJpbnRIYW5kbGVyKGUsbCx1KSxsPXUtMTticmVha31pZigrK3U+PXR8fChuPWVbdV0pPDMyfHxuPjEyNiYmbjxoKXt0aGlzLl9wcmludEhhbmRsZXIoZSxsLHUpLGw9dS0xO2JyZWFrfWlmKCsrdT49dHx8KG49ZVt1XSk8MzJ8fG4+MTI2JiZuPGgpe3RoaXMuX3ByaW50SGFuZGxlcihlLGwsdSksbD11LTE7YnJlYWt9aWYoKyt1Pj10fHwobj1lW3VdKTwzMnx8bj4xMjYmJm48aCl7dGhpcy5fcHJpbnRIYW5kbGVyKGUsbCx1KSxsPXUtMTticmVha319YnJlYWs7Y2FzZSAzOnRoaXMuX2V4ZWN1dGVIYW5kbGVyc1tuXT90aGlzLl9leGVjdXRlSGFuZGxlcnNbbl0oKTp0aGlzLl9leGVjdXRlSGFuZGxlckZiKG4pLHRoaXMucHJlY2VkaW5nQ29kZXBvaW50PTA7YnJlYWs7Y2FzZSAwOmJyZWFrO2Nhc2UgMTppZih0aGlzLl9lcnJvckhhbmRsZXIoe3Bvc2l0aW9uOmwsY29kZTpuLGN1cnJlbnRTdGF0ZTp0aGlzLmN1cnJlbnRTdGF0ZSxjb2xsZWN0OnRoaXMuX2NvbGxlY3QscGFyYW1zOnRoaXMuX3BhcmFtcyxhYm9ydDohMX0pLmFib3J0KXJldHVybjticmVhaztjYXNlIDc6Zm9yKHZhciBmPShhPXRoaXMuX2NzaUhhbmRsZXJzW3RoaXMuX2NvbGxlY3Q8PDh8bl0pP2EubGVuZ3RoLTE6LTE7Zj49MCYmITAhPT0oaT1hW2ZdKHRoaXMuX3BhcmFtcykpO2YtLSlpZihpIGluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gdGhpcy5fcHJlc2VydmVTdGFjaygzLGEsZixvLGwpLGk7ZjwwJiZ0aGlzLl9jc2lIYW5kbGVyRmIodGhpcy5fY29sbGVjdDw8OHxuLHRoaXMuX3BhcmFtcyksdGhpcy5wcmVjZWRpbmdDb2RlcG9pbnQ9MDticmVhaztjYXNlIDg6ZG97c3dpdGNoKG4pe2Nhc2UgNTk6dGhpcy5fcGFyYW1zLmFkZFBhcmFtKDApO2JyZWFrO2Nhc2UgNTg6dGhpcy5fcGFyYW1zLmFkZFN1YlBhcmFtKC0xKTticmVhaztkZWZhdWx0OnRoaXMuX3BhcmFtcy5hZGREaWdpdChuLTQ4KX19d2hpbGUoKytsPHQmJihuPWVbbF0pPjQ3JiZuPDYwKTtsLS07YnJlYWs7Y2FzZSA5OnRoaXMuX2NvbGxlY3Q8PD04LHRoaXMuX2NvbGxlY3R8PW47YnJlYWs7Y2FzZSAxMDpmb3IodmFyIF89dGhpcy5fZXNjSGFuZGxlcnNbdGhpcy5fY29sbGVjdDw8OHxuXSxkPV8/Xy5sZW5ndGgtMTotMTtkPj0wJiYhMCE9PShpPV9bZF0oKSk7ZC0tKWlmKGkgaW5zdGFuY2VvZiBQcm9taXNlKXJldHVybiB0aGlzLl9wcmVzZXJ2ZVN0YWNrKDQsXyxkLG8sbCksaTtkPDAmJnRoaXMuX2VzY0hhbmRsZXJGYih0aGlzLl9jb2xsZWN0PDw4fG4pLHRoaXMucHJlY2VkaW5nQ29kZXBvaW50PTA7YnJlYWs7Y2FzZSAxMTp0aGlzLl9wYXJhbXMucmVzZXQoKSx0aGlzLl9wYXJhbXMuYWRkUGFyYW0oMCksdGhpcy5fY29sbGVjdD0wO2JyZWFrO2Nhc2UgMTI6dGhpcy5fZGNzUGFyc2VyLmhvb2sodGhpcy5fY29sbGVjdDw8OHxuLHRoaXMuX3BhcmFtcyk7YnJlYWs7Y2FzZSAxMzpmb3IodmFyIHA9bCsxOzsrK3ApaWYocD49dHx8MjQ9PT0obj1lW3BdKXx8MjY9PT1ufHwyNz09PW58fG4+MTI3JiZuPGgpe3RoaXMuX2Rjc1BhcnNlci5wdXQoZSxsLHApLGw9cC0xO2JyZWFrfWJyZWFrO2Nhc2UgMTQ6aWYoaT10aGlzLl9kY3NQYXJzZXIudW5ob29rKDI0IT09biYmMjYhPT1uKSlyZXR1cm4gdGhpcy5fcHJlc2VydmVTdGFjayg2LFtdLDAsbyxsKSxpOzI3PT09biYmKG98PTEpLHRoaXMuX3BhcmFtcy5yZXNldCgpLHRoaXMuX3BhcmFtcy5hZGRQYXJhbSgwKSx0aGlzLl9jb2xsZWN0PTAsdGhpcy5wcmVjZWRpbmdDb2RlcG9pbnQ9MDticmVhaztjYXNlIDQ6dGhpcy5fb3NjUGFyc2VyLnN0YXJ0KCk7YnJlYWs7Y2FzZSA1OmZvcih2YXIgdj1sKzE7O3YrKylpZih2Pj10fHwobj1lW3ZdKTwzMnx8bj4xMjcmJm48aCl7dGhpcy5fb3NjUGFyc2VyLnB1dChlLGwsdiksbD12LTE7YnJlYWt9YnJlYWs7Y2FzZSA2OmlmKGk9dGhpcy5fb3NjUGFyc2VyLmVuZCgyNCE9PW4mJjI2IT09bikpcmV0dXJuIHRoaXMuX3ByZXNlcnZlU3RhY2soNSxbXSwwLG8sbCksaTsyNz09PW4mJihvfD0xKSx0aGlzLl9wYXJhbXMucmVzZXQoKSx0aGlzLl9wYXJhbXMuYWRkUGFyYW0oMCksdGhpcy5fY29sbGVjdD0wLHRoaXMucHJlY2VkaW5nQ29kZXBvaW50PTB9dGhpcy5jdXJyZW50U3RhdGU9MTUmb319LHJ9KG8uRGlzcG9zYWJsZSk7dC5Fc2NhcGVTZXF1ZW5jZVBhcnNlcj1mfSw2MjQyOihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Pc2NIYW5kbGVyPXQuT3NjUGFyc2VyPXZvaWQgMDt2YXIgaT1yKDU3NzApLG49cig0ODIpLG89W10scz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt0aGlzLl9zdGF0ZT0wLHRoaXMuX2FjdGl2ZT1vLHRoaXMuX2lkPS0xLHRoaXMuX2hhbmRsZXJzPU9iamVjdC5jcmVhdGUobnVsbCksdGhpcy5faGFuZGxlckZiPWZ1bmN0aW9uKCl7fSx0aGlzLl9zdGFjaz17cGF1c2VkOiExLGxvb3BQb3NpdGlvbjowLGZhbGxUaHJvdWdoOiExfX1yZXR1cm4gZS5wcm90b3R5cGUucmVnaXN0ZXJIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7dm9pZCAwPT09dGhpcy5faGFuZGxlcnNbZV0mJih0aGlzLl9oYW5kbGVyc1tlXT1bXSk7dmFyIHI9dGhpcy5faGFuZGxlcnNbZV07cmV0dXJuIHIucHVzaCh0KSx7ZGlzcG9zZTpmdW5jdGlvbigpe3ZhciBlPXIuaW5kZXhPZih0KTstMSE9PWUmJnIuc3BsaWNlKGUsMSl9fX0sZS5wcm90b3R5cGUuY2xlYXJIYW5kbGVyPWZ1bmN0aW9uKGUpe3RoaXMuX2hhbmRsZXJzW2VdJiZkZWxldGUgdGhpcy5faGFuZGxlcnNbZV19LGUucHJvdG90eXBlLnNldEhhbmRsZXJGYWxsYmFjaz1mdW5jdGlvbihlKXt0aGlzLl9oYW5kbGVyRmI9ZX0sZS5wcm90b3R5cGUuZGlzcG9zZT1mdW5jdGlvbigpe3RoaXMuX2hhbmRsZXJzPU9iamVjdC5jcmVhdGUobnVsbCksdGhpcy5faGFuZGxlckZiPWZ1bmN0aW9uKCl7fSx0aGlzLl9hY3RpdmU9b30sZS5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXtpZigyPT09dGhpcy5fc3RhdGUpZm9yKHZhciBlPXRoaXMuX3N0YWNrLnBhdXNlZD90aGlzLl9zdGFjay5sb29wUG9zaXRpb24tMTp0aGlzLl9hY3RpdmUubGVuZ3RoLTE7ZT49MDstLWUpdGhpcy5fYWN0aXZlW2VdLmVuZCghMSk7dGhpcy5fc3RhY2sucGF1c2VkPSExLHRoaXMuX2FjdGl2ZT1vLHRoaXMuX2lkPS0xLHRoaXMuX3N0YXRlPTB9LGUucHJvdG90eXBlLl9zdGFydD1mdW5jdGlvbigpe2lmKHRoaXMuX2FjdGl2ZT10aGlzLl9oYW5kbGVyc1t0aGlzLl9pZF18fG8sdGhpcy5fYWN0aXZlLmxlbmd0aClmb3IodmFyIGU9dGhpcy5fYWN0aXZlLmxlbmd0aC0xO2U+PTA7ZS0tKXRoaXMuX2FjdGl2ZVtlXS5zdGFydCgpO2Vsc2UgdGhpcy5faGFuZGxlckZiKHRoaXMuX2lkLCJTVEFSVCIpfSxlLnByb3RvdHlwZS5fcHV0PWZ1bmN0aW9uKGUsdCxyKXtpZih0aGlzLl9hY3RpdmUubGVuZ3RoKWZvcih2YXIgaT10aGlzLl9hY3RpdmUubGVuZ3RoLTE7aT49MDtpLS0pdGhpcy5fYWN0aXZlW2ldLnB1dChlLHQscik7ZWxzZSB0aGlzLl9oYW5kbGVyRmIodGhpcy5faWQsIlBVVCIsKDAsbi51dGYzMlRvU3RyaW5nKShlLHQscikpfSxlLnByb3RvdHlwZS5zdGFydD1mdW5jdGlvbigpe3RoaXMucmVzZXQoKSx0aGlzLl9zdGF0ZT0xfSxlLnByb3RvdHlwZS5wdXQ9ZnVuY3Rpb24oZSx0LHIpe2lmKDMhPT10aGlzLl9zdGF0ZSl7aWYoMT09PXRoaXMuX3N0YXRlKWZvcig7dDxyOyl7dmFyIGk9ZVt0KytdO2lmKDU5PT09aSl7dGhpcy5fc3RhdGU9Mix0aGlzLl9zdGFydCgpO2JyZWFrfWlmKGk8NDh8fDU3PGkpcmV0dXJuIHZvaWQodGhpcy5fc3RhdGU9Myk7LTE9PT10aGlzLl9pZCYmKHRoaXMuX2lkPTApLHRoaXMuX2lkPTEwKnRoaXMuX2lkK2ktNDh9Mj09PXRoaXMuX3N0YXRlJiZyLXQ+MCYmdGhpcy5fcHV0KGUsdCxyKX19LGUucHJvdG90eXBlLmVuZD1mdW5jdGlvbihlLHQpe2lmKHZvaWQgMD09PXQmJih0PSEwKSwwIT09dGhpcy5fc3RhdGUpe2lmKDMhPT10aGlzLl9zdGF0ZSlpZigxPT09dGhpcy5fc3RhdGUmJnRoaXMuX3N0YXJ0KCksdGhpcy5fYWN0aXZlLmxlbmd0aCl7dmFyIHI9ITEsaT10aGlzLl9hY3RpdmUubGVuZ3RoLTEsbj0hMTtpZih0aGlzLl9zdGFjay5wYXVzZWQmJihpPXRoaXMuX3N0YWNrLmxvb3BQb3NpdGlvbi0xLHI9dCxuPXRoaXMuX3N0YWNrLmZhbGxUaHJvdWdoLHRoaXMuX3N0YWNrLnBhdXNlZD0hMSksIW4mJiExPT09cil7Zm9yKDtpPj0wJiYhMCE9PShyPXRoaXMuX2FjdGl2ZVtpXS5lbmQoZSkpO2ktLSlpZihyIGluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gdGhpcy5fc3RhY2sucGF1c2VkPSEwLHRoaXMuX3N0YWNrLmxvb3BQb3NpdGlvbj1pLHRoaXMuX3N0YWNrLmZhbGxUaHJvdWdoPSExLHI7aS0tfWZvcig7aT49MDtpLS0paWYoKHI9dGhpcy5fYWN0aXZlW2ldLmVuZCghMSkpaW5zdGFuY2VvZiBQcm9taXNlKXJldHVybiB0aGlzLl9zdGFjay5wYXVzZWQ9ITAsdGhpcy5fc3RhY2subG9vcFBvc2l0aW9uPWksdGhpcy5fc3RhY2suZmFsbFRocm91Z2g9ITAscn1lbHNlIHRoaXMuX2hhbmRsZXJGYih0aGlzLl9pZCwiRU5EIixlKTt0aGlzLl9hY3RpdmU9byx0aGlzLl9pZD0tMSx0aGlzLl9zdGF0ZT0wfX0sZX0oKTt0Lk9zY1BhcnNlcj1zO3ZhciBhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9oYW5kbGVyPWUsdGhpcy5fZGF0YT0iIix0aGlzLl9oaXRMaW1pdD0hMX1yZXR1cm4gZS5wcm90b3R5cGUuc3RhcnQ9ZnVuY3Rpb24oKXt0aGlzLl9kYXRhPSIiLHRoaXMuX2hpdExpbWl0PSExfSxlLnByb3RvdHlwZS5wdXQ9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2hpdExpbWl0fHwodGhpcy5fZGF0YSs9KDAsbi51dGYzMlRvU3RyaW5nKShlLHQsciksdGhpcy5fZGF0YS5sZW5ndGg+aS5QQVlMT0FEX0xJTUlUJiYodGhpcy5fZGF0YT0iIix0aGlzLl9oaXRMaW1pdD0hMCkpfSxlLnByb3RvdHlwZS5lbmQ9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcyxyPSExO2lmKHRoaXMuX2hpdExpbWl0KXI9ITE7ZWxzZSBpZihlJiYocj10aGlzLl9oYW5kbGVyKHRoaXMuX2RhdGEpKWluc3RhbmNlb2YgUHJvbWlzZSlyZXR1cm4gci50aGVuKChmdW5jdGlvbihlKXtyZXR1cm4gdC5fZGF0YT0iIix0Ll9oaXRMaW1pdD0hMSxlfSkpO3JldHVybiB0aGlzLl9kYXRhPSIiLHRoaXMuX2hpdExpbWl0PSExLHJ9LGV9KCk7dC5Pc2NIYW5kbGVyPWF9LDg3NDI6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5QYXJhbXM9dm9pZCAwO3ZhciByPTIxNDc0ODM2NDcsaT1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSx0KXtpZih2b2lkIDA9PT1lJiYoZT0zMiksdm9pZCAwPT09dCYmKHQ9MzIpLHRoaXMubWF4TGVuZ3RoPWUsdGhpcy5tYXhTdWJQYXJhbXNMZW5ndGg9dCx0PjI1Nil0aHJvdyBuZXcgRXJyb3IoIm1heFN1YlBhcmFtc0xlbmd0aCBtdXN0IG5vdCBiZSBncmVhdGVyIHRoYW4gMjU2Iik7dGhpcy5wYXJhbXM9bmV3IEludDMyQXJyYXkoZSksdGhpcy5sZW5ndGg9MCx0aGlzLl9zdWJQYXJhbXM9bmV3IEludDMyQXJyYXkodCksdGhpcy5fc3ViUGFyYW1zTGVuZ3RoPTAsdGhpcy5fc3ViUGFyYW1zSWR4PW5ldyBVaW50MTZBcnJheShlKSx0aGlzLl9yZWplY3REaWdpdHM9ITEsdGhpcy5fcmVqZWN0U3ViRGlnaXRzPSExLHRoaXMuX2RpZ2l0SXNTdWI9ITF9cmV0dXJuIGUuZnJvbUFycmF5PWZ1bmN0aW9uKHQpe3ZhciByPW5ldyBlO2lmKCF0Lmxlbmd0aClyZXR1cm4gcjtmb3IodmFyIGk9QXJyYXkuaXNBcnJheSh0WzBdKT8xOjA7aTx0Lmxlbmd0aDsrK2kpe3ZhciBuPXRbaV07aWYoQXJyYXkuaXNBcnJheShuKSlmb3IodmFyIG89MDtvPG4ubGVuZ3RoOysrbylyLmFkZFN1YlBhcmFtKG5bb10pO2Vsc2Ugci5hZGRQYXJhbShuKX1yZXR1cm4gcn0sZS5wcm90b3R5cGUuY2xvbmU9ZnVuY3Rpb24oKXt2YXIgdD1uZXcgZSh0aGlzLm1heExlbmd0aCx0aGlzLm1heFN1YlBhcmFtc0xlbmd0aCk7cmV0dXJuIHQucGFyYW1zLnNldCh0aGlzLnBhcmFtcyksdC5sZW5ndGg9dGhpcy5sZW5ndGgsdC5fc3ViUGFyYW1zLnNldCh0aGlzLl9zdWJQYXJhbXMpLHQuX3N1YlBhcmFtc0xlbmd0aD10aGlzLl9zdWJQYXJhbXNMZW5ndGgsdC5fc3ViUGFyYW1zSWR4LnNldCh0aGlzLl9zdWJQYXJhbXNJZHgpLHQuX3JlamVjdERpZ2l0cz10aGlzLl9yZWplY3REaWdpdHMsdC5fcmVqZWN0U3ViRGlnaXRzPXRoaXMuX3JlamVjdFN1YkRpZ2l0cyx0Ll9kaWdpdElzU3ViPXRoaXMuX2RpZ2l0SXNTdWIsdH0sZS5wcm90b3R5cGUudG9BcnJheT1mdW5jdGlvbigpe2Zvcih2YXIgZT1bXSx0PTA7dDx0aGlzLmxlbmd0aDsrK3Qpe2UucHVzaCh0aGlzLnBhcmFtc1t0XSk7dmFyIHI9dGhpcy5fc3ViUGFyYW1zSWR4W3RdPj44LGk9MjU1JnRoaXMuX3N1YlBhcmFtc0lkeFt0XTtpLXI+MCYmZS5wdXNoKEFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHRoaXMuX3N1YlBhcmFtcyxyLGkpKX1yZXR1cm4gZX0sZS5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLmxlbmd0aD0wLHRoaXMuX3N1YlBhcmFtc0xlbmd0aD0wLHRoaXMuX3JlamVjdERpZ2l0cz0hMSx0aGlzLl9yZWplY3RTdWJEaWdpdHM9ITEsdGhpcy5fZGlnaXRJc1N1Yj0hMX0sZS5wcm90b3R5cGUuYWRkUGFyYW09ZnVuY3Rpb24oZSl7aWYodGhpcy5fZGlnaXRJc1N1Yj0hMSx0aGlzLmxlbmd0aD49dGhpcy5tYXhMZW5ndGgpdGhpcy5fcmVqZWN0RGlnaXRzPSEwO2Vsc2V7aWYoZTwtMSl0aHJvdyBuZXcgRXJyb3IoInZhbHVlcyBsZXNzZXIgdGhhbiAtMSBhcmUgbm90IGFsbG93ZWQiKTt0aGlzLl9zdWJQYXJhbXNJZHhbdGhpcy5sZW5ndGhdPXRoaXMuX3N1YlBhcmFtc0xlbmd0aDw8OHx0aGlzLl9zdWJQYXJhbXNMZW5ndGgsdGhpcy5wYXJhbXNbdGhpcy5sZW5ndGgrK109ZT5yP3I6ZX19LGUucHJvdG90eXBlLmFkZFN1YlBhcmFtPWZ1bmN0aW9uKGUpe2lmKHRoaXMuX2RpZ2l0SXNTdWI9ITAsdGhpcy5sZW5ndGgpaWYodGhpcy5fcmVqZWN0RGlnaXRzfHx0aGlzLl9zdWJQYXJhbXNMZW5ndGg+PXRoaXMubWF4U3ViUGFyYW1zTGVuZ3RoKXRoaXMuX3JlamVjdFN1YkRpZ2l0cz0hMDtlbHNle2lmKGU8LTEpdGhyb3cgbmV3IEVycm9yKCJ2YWx1ZXMgbGVzc2VyIHRoYW4gLTEgYXJlIG5vdCBhbGxvd2VkIik7dGhpcy5fc3ViUGFyYW1zW3RoaXMuX3N1YlBhcmFtc0xlbmd0aCsrXT1lPnI/cjplLHRoaXMuX3N1YlBhcmFtc0lkeFt0aGlzLmxlbmd0aC0xXSsrfX0sZS5wcm90b3R5cGUuaGFzU3ViUGFyYW1zPWZ1bmN0aW9uKGUpe3JldHVybigyNTUmdGhpcy5fc3ViUGFyYW1zSWR4W2VdKS0odGhpcy5fc3ViUGFyYW1zSWR4W2VdPj44KT4wfSxlLnByb3RvdHlwZS5nZXRTdWJQYXJhbXM9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fc3ViUGFyYW1zSWR4W2VdPj44LHI9MjU1JnRoaXMuX3N1YlBhcmFtc0lkeFtlXTtyZXR1cm4gci10PjA/dGhpcy5fc3ViUGFyYW1zLnN1YmFycmF5KHQscik6bnVsbH0sZS5wcm90b3R5cGUuZ2V0U3ViUGFyYW1zQWxsPWZ1bmN0aW9uKCl7Zm9yKHZhciBlPXt9LHQ9MDt0PHRoaXMubGVuZ3RoOysrdCl7dmFyIHI9dGhpcy5fc3ViUGFyYW1zSWR4W3RdPj44LGk9MjU1JnRoaXMuX3N1YlBhcmFtc0lkeFt0XTtpLXI+MCYmKGVbdF09dGhpcy5fc3ViUGFyYW1zLnNsaWNlKHIsaSkpfXJldHVybiBlfSxlLnByb3RvdHlwZS5hZGREaWdpdD1mdW5jdGlvbihlKXt2YXIgdDtpZighKHRoaXMuX3JlamVjdERpZ2l0c3x8ISh0PXRoaXMuX2RpZ2l0SXNTdWI/dGhpcy5fc3ViUGFyYW1zTGVuZ3RoOnRoaXMubGVuZ3RoKXx8dGhpcy5fZGlnaXRJc1N1YiYmdGhpcy5fcmVqZWN0U3ViRGlnaXRzKSl7dmFyIGk9dGhpcy5fZGlnaXRJc1N1Yj90aGlzLl9zdWJQYXJhbXM6dGhpcy5wYXJhbXMsbj1pW3QtMV07aVt0LTFdPX5uP01hdGgubWluKDEwKm4rZSxyKTplfX0sZX0oKTt0LlBhcmFtcz1pfSw1NzQxOihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQWRkb25NYW5hZ2VyPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoKXt0aGlzLl9hZGRvbnM9W119cmV0dXJuIGUucHJvdG90eXBlLmRpc3Bvc2U9ZnVuY3Rpb24oKXtmb3IodmFyIGU9dGhpcy5fYWRkb25zLmxlbmd0aC0xO2U+PTA7ZS0tKXRoaXMuX2FkZG9uc1tlXS5pbnN0YW5jZS5kaXNwb3NlKCl9LGUucHJvdG90eXBlLmxvYWRBZGRvbj1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMsaT17aW5zdGFuY2U6dCxkaXNwb3NlOnQuZGlzcG9zZSxpc0Rpc3Bvc2VkOiExfTt0aGlzLl9hZGRvbnMucHVzaChpKSx0LmRpc3Bvc2U9ZnVuY3Rpb24oKXtyZXR1cm4gci5fd3JhcHBlZEFkZG9uRGlzcG9zZShpKX0sdC5hY3RpdmF0ZShlKX0sZS5wcm90b3R5cGUuX3dyYXBwZWRBZGRvbkRpc3Bvc2U9ZnVuY3Rpb24oZSl7aWYoIWUuaXNEaXNwb3NlZCl7Zm9yKHZhciB0PS0xLHI9MDtyPHRoaXMuX2FkZG9ucy5sZW5ndGg7cisrKWlmKHRoaXMuX2FkZG9uc1tyXT09PWUpe3Q9cjticmVha31pZigtMT09PXQpdGhyb3cgbmV3IEVycm9yKCJDb3VsZCBub3QgZGlzcG9zZSBhbiBhZGRvbiB0aGF0IGhhcyBub3QgYmVlbiBsb2FkZWQiKTtlLmlzRGlzcG9zZWQ9ITAsZS5kaXNwb3NlLmFwcGx5KGUuaW5zdGFuY2UpLHRoaXMuX2FkZG9ucy5zcGxpY2UodCwxKX19LGV9KCk7dC5BZGRvbk1hbmFnZXI9cn0sODc3MTooZSx0LHIpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQnVmZmVyQXBpVmlldz12b2lkIDA7dmFyIGk9cigzNzg1KSxuPXIoNTExKSxvPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQpe3RoaXMuX2J1ZmZlcj1lLHRoaXMudHlwZT10fXJldHVybiBlLnByb3RvdHlwZS5pbml0PWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9idWZmZXI9ZSx0aGlzfSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImN1cnNvclkiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYnVmZmVyLnl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJjdXJzb3JYIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2J1ZmZlci54fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwidmlld3BvcnRZIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2J1ZmZlci55ZGlzcH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImJhc2VZIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2J1ZmZlci55YmFzZX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImxlbmd0aCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9idWZmZXIubGluZXMubGVuZ3RofSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmdldExpbmU9ZnVuY3Rpb24oZSl7dmFyIHQ9dGhpcy5fYnVmZmVyLmxpbmVzLmdldChlKTtpZih0KXJldHVybiBuZXcgaS5CdWZmZXJMaW5lQXBpVmlldyh0KX0sZS5wcm90b3R5cGUuZ2V0TnVsbENlbGw9ZnVuY3Rpb24oKXtyZXR1cm4gbmV3IG4uQ2VsbERhdGF9LGV9KCk7dC5CdWZmZXJBcGlWaWV3PW99LDM3ODU6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJ1ZmZlckxpbmVBcGlWaWV3PXZvaWQgMDt2YXIgaT1yKDUxMSksbj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fbGluZT1lfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImlzV3JhcHBlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9saW5lLmlzV3JhcHBlZH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImxlbmd0aCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9saW5lLmxlbmd0aH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5nZXRDZWxsPWZ1bmN0aW9uKGUsdCl7aWYoIShlPDB8fGU+PXRoaXMuX2xpbmUubGVuZ3RoKSlyZXR1cm4gdD8odGhpcy5fbGluZS5sb2FkQ2VsbChlLHQpLHQpOnRoaXMuX2xpbmUubG9hZENlbGwoZSxuZXcgaS5DZWxsRGF0YSl9LGUucHJvdG90eXBlLnRyYW5zbGF0ZVRvU3RyaW5nPWZ1bmN0aW9uKGUsdCxyKXtyZXR1cm4gdGhpcy5fbGluZS50cmFuc2xhdGVUb1N0cmluZyhlLHQscil9LGV9KCk7dC5CdWZmZXJMaW5lQXBpVmlldz1ufSw4Mjg1OihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5CdWZmZXJOYW1lc3BhY2VBcGk9dm9pZCAwO3ZhciBpPXIoODc3MSksbj1yKDg0NjApLG89ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe3ZhciB0PXRoaXM7dGhpcy5fY29yZT1lLHRoaXMuX29uQnVmZmVyQ2hhbmdlPW5ldyBuLkV2ZW50RW1pdHRlcix0aGlzLl9ub3JtYWw9bmV3IGkuQnVmZmVyQXBpVmlldyh0aGlzLl9jb3JlLmJ1ZmZlcnMubm9ybWFsLCJub3JtYWwiKSx0aGlzLl9hbHRlcm5hdGU9bmV3IGkuQnVmZmVyQXBpVmlldyh0aGlzLl9jb3JlLmJ1ZmZlcnMuYWx0LCJhbHRlcm5hdGUiKSx0aGlzLl9jb3JlLmJ1ZmZlcnMub25CdWZmZXJBY3RpdmF0ZSgoZnVuY3Rpb24oKXtyZXR1cm4gdC5fb25CdWZmZXJDaGFuZ2UuZmlyZSh0LmFjdGl2ZSl9KSl9cmV0dXJuIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25CdWZmZXJDaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25CdWZmZXJDaGFuZ2UuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJhY3RpdmUiLHtnZXQ6ZnVuY3Rpb24oKXtpZih0aGlzLl9jb3JlLmJ1ZmZlcnMuYWN0aXZlPT09dGhpcy5fY29yZS5idWZmZXJzLm5vcm1hbClyZXR1cm4gdGhpcy5ub3JtYWw7aWYodGhpcy5fY29yZS5idWZmZXJzLmFjdGl2ZT09PXRoaXMuX2NvcmUuYnVmZmVycy5hbHQpcmV0dXJuIHRoaXMuYWx0ZXJuYXRlO3Rocm93IG5ldyBFcnJvcigiQWN0aXZlIGJ1ZmZlciBpcyBuZWl0aGVyIG5vcm1hbCBub3IgYWx0ZXJuYXRlIil9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJub3JtYWwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fbm9ybWFsLmluaXQodGhpcy5fY29yZS5idWZmZXJzLm5vcm1hbCl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJhbHRlcm5hdGUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYWx0ZXJuYXRlLmluaXQodGhpcy5fY29yZS5idWZmZXJzLmFsdCl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZX0oKTt0LkJ1ZmZlck5hbWVzcGFjZUFwaT1vfSw3OTc1OihlLHQpPT57T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuUGFyc2VyQXBpPXZvaWQgMDt2YXIgcj1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fY29yZT1lfXJldHVybiBlLnByb3RvdHlwZS5yZWdpc3RlckNzaUhhbmRsZXI9ZnVuY3Rpb24oZSx0KXtyZXR1cm4gdGhpcy5fY29yZS5yZWdpc3RlckNzaUhhbmRsZXIoZSwoZnVuY3Rpb24oZSl7cmV0dXJuIHQoZS50b0FycmF5KCkpfSkpfSxlLnByb3RvdHlwZS5hZGRDc2lIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMucmVnaXN0ZXJDc2lIYW5kbGVyKGUsdCl9LGUucHJvdG90eXBlLnJlZ2lzdGVyRGNzSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9jb3JlLnJlZ2lzdGVyRGNzSGFuZGxlcihlLChmdW5jdGlvbihlLHIpe3JldHVybiB0KGUsci50b0FycmF5KCkpfSkpfSxlLnByb3RvdHlwZS5hZGREY3NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMucmVnaXN0ZXJEY3NIYW5kbGVyKGUsdCl9LGUucHJvdG90eXBlLnJlZ2lzdGVyRXNjSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9jb3JlLnJlZ2lzdGVyRXNjSGFuZGxlcihlLHQpfSxlLnByb3RvdHlwZS5hZGRFc2NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMucmVnaXN0ZXJFc2NIYW5kbGVyKGUsdCl9LGUucHJvdG90eXBlLnJlZ2lzdGVyT3NjSGFuZGxlcj1mdW5jdGlvbihlLHQpe3JldHVybiB0aGlzLl9jb3JlLnJlZ2lzdGVyT3NjSGFuZGxlcihlLHQpfSxlLnByb3RvdHlwZS5hZGRPc2NIYW5kbGVyPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIHRoaXMucmVnaXN0ZXJPc2NIYW5kbGVyKGUsdCl9LGV9KCk7dC5QYXJzZXJBcGk9cn0sNzA5MDooZSx0KT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LlVuaWNvZGVBcGk9dm9pZCAwO3ZhciByPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt0aGlzLl9jb3JlPWV9cmV0dXJuIGUucHJvdG90eXBlLnJlZ2lzdGVyPWZ1bmN0aW9uKGUpe3RoaXMuX2NvcmUudW5pY29kZVNlcnZpY2UucmVnaXN0ZXIoZSl9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwidmVyc2lvbnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS51bmljb2RlU2VydmljZS52ZXJzaW9uc30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImFjdGl2ZVZlcnNpb24iLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS51bmljb2RlU2VydmljZS5hY3RpdmVWZXJzaW9ufSxzZXQ6ZnVuY3Rpb24oZSl7dGhpcy5fY29yZS51bmljb2RlU2VydmljZS5hY3RpdmVWZXJzaW9uPWV9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZX0oKTt0LlVuaWNvZGVBcGk9cn0sNzQ0OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaSxuPXRoaXMmJnRoaXMuX19leHRlbmRzfHwoaT1mdW5jdGlvbihlLHQpe3JldHVybiBpPU9iamVjdC5zZXRQcm90b3R5cGVPZnx8e19fcHJvdG9fXzpbXX1pbnN0YW5jZW9mIEFycmF5JiZmdW5jdGlvbihlLHQpe2UuX19wcm90b19fPXR9fHxmdW5jdGlvbihlLHQpe2Zvcih2YXIgciBpbiB0KU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LHIpJiYoZVtyXT10W3JdKX0saShlLHQpfSxmdW5jdGlvbihlLHQpe2lmKCJmdW5jdGlvbiIhPXR5cGVvZiB0JiZudWxsIT09dCl0aHJvdyBuZXcgVHlwZUVycm9yKCJDbGFzcyBleHRlbmRzIHZhbHVlICIrU3RyaW5nKHQpKyIgaXMgbm90IGEgY29uc3RydWN0b3Igb3IgbnVsbCIpO2Z1bmN0aW9uIHIoKXt0aGlzLmNvbnN0cnVjdG9yPWV9aShlLHQpLGUucHJvdG90eXBlPW51bGw9PT10P09iamVjdC5jcmVhdGUodCk6KHIucHJvdG90eXBlPXQucHJvdG90eXBlLG5ldyByKX0pLG89dGhpcyYmdGhpcy5fX2RlY29yYXRlfHxmdW5jdGlvbihlLHQscixpKXt2YXIgbixvPWFyZ3VtZW50cy5sZW5ndGgscz1vPDM/dDpudWxsPT09aT9pPU9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodCxyKTppO2lmKCJvYmplY3QiPT10eXBlb2YgUmVmbGVjdCYmImZ1bmN0aW9uIj09dHlwZW9mIFJlZmxlY3QuZGVjb3JhdGUpcz1SZWZsZWN0LmRlY29yYXRlKGUsdCxyLGkpO2Vsc2UgZm9yKHZhciBhPWUubGVuZ3RoLTE7YT49MDthLS0pKG49ZVthXSkmJihzPShvPDM/bihzKTpvPjM/bih0LHIscyk6bih0LHIpKXx8cyk7cmV0dXJuIG8+MyYmcyYmT2JqZWN0LmRlZmluZVByb3BlcnR5KHQscixzKSxzfSxzPXRoaXMmJnRoaXMuX19wYXJhbXx8ZnVuY3Rpb24oZSx0KXtyZXR1cm4gZnVuY3Rpb24ocixpKXt0KHIsaSxlKX19O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkJ1ZmZlclNlcnZpY2U9dC5NSU5JTVVNX1JPV1M9dC5NSU5JTVVNX0NPTFM9dm9pZCAwO3ZhciBhPXIoMjU4NSksYz1yKDUyOTUpLGw9cig4NDYwKSx1PXIoODQ0KTt0Lk1JTklNVU1fQ09MUz0yLHQuTUlOSU1VTV9ST1dTPTE7dmFyIGg9ZnVuY3Rpb24oZSl7ZnVuY3Rpb24gcihyKXt2YXIgaT1lLmNhbGwodGhpcyl8fHRoaXM7cmV0dXJuIGkuX29wdGlvbnNTZXJ2aWNlPXIsaS5pc1VzZXJTY3JvbGxpbmc9ITEsaS5fb25SZXNpemU9bmV3IGwuRXZlbnRFbWl0dGVyLGkuX29uU2Nyb2xsPW5ldyBsLkV2ZW50RW1pdHRlcixpLmNvbHM9TWF0aC5tYXgoci5vcHRpb25zLmNvbHN8fDAsdC5NSU5JTVVNX0NPTFMpLGkucm93cz1NYXRoLm1heChyLm9wdGlvbnMucm93c3x8MCx0Lk1JTklNVU1fUk9XUyksaS5idWZmZXJzPW5ldyBjLkJ1ZmZlclNldChyLGkpLGl9cmV0dXJuIG4ocixlKSxPYmplY3QuZGVmaW5lUHJvcGVydHkoci5wcm90b3R5cGUsIm9uUmVzaXplIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uUmVzaXplLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShyLnByb3RvdHlwZSwib25TY3JvbGwiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25TY3JvbGwuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KHIucHJvdG90eXBlLCJidWZmZXIiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5idWZmZXJzLmFjdGl2ZX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxyLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7ZS5wcm90b3R5cGUuZGlzcG9zZS5jYWxsKHRoaXMpLHRoaXMuYnVmZmVycy5kaXNwb3NlKCl9LHIucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuY29scz1lLHRoaXMucm93cz10LHRoaXMuYnVmZmVycy5yZXNpemUoZSx0KSx0aGlzLmJ1ZmZlcnMuc2V0dXBUYWJTdG9wcyh0aGlzLmNvbHMpLHRoaXMuX29uUmVzaXplLmZpcmUoe2NvbHM6ZSxyb3dzOnR9KX0sci5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLmJ1ZmZlcnMucmVzZXQoKSx0aGlzLmlzVXNlclNjcm9sbGluZz0hMX0sci5wcm90b3R5cGUuc2Nyb2xsPWZ1bmN0aW9uKGUsdCl7dm9pZCAwPT09dCYmKHQ9ITEpO3ZhciByLGk9dGhpcy5idWZmZXI7KHI9dGhpcy5fY2FjaGVkQmxhbmtMaW5lKSYmci5sZW5ndGg9PT10aGlzLmNvbHMmJnIuZ2V0RmcoMCk9PT1lLmZnJiZyLmdldEJnKDApPT09ZS5iZ3x8KHI9aS5nZXRCbGFua0xpbmUoZSx0KSx0aGlzLl9jYWNoZWRCbGFua0xpbmU9ciksci5pc1dyYXBwZWQ9dDt2YXIgbj1pLnliYXNlK2kuc2Nyb2xsVG9wLG89aS55YmFzZStpLnNjcm9sbEJvdHRvbTtpZigwPT09aS5zY3JvbGxUb3Ape3ZhciBzPWkubGluZXMuaXNGdWxsO289PT1pLmxpbmVzLmxlbmd0aC0xP3M/aS5saW5lcy5yZWN5Y2xlKCkuY29weUZyb20ocik6aS5saW5lcy5wdXNoKHIuY2xvbmUoKSk6aS5saW5lcy5zcGxpY2UobysxLDAsci5jbG9uZSgpKSxzP3RoaXMuaXNVc2VyU2Nyb2xsaW5nJiYoaS55ZGlzcD1NYXRoLm1heChpLnlkaXNwLTEsMCkpOihpLnliYXNlKyssdGhpcy5pc1VzZXJTY3JvbGxpbmd8fGkueWRpc3ArKyl9ZWxzZXt2YXIgYT1vLW4rMTtpLmxpbmVzLnNoaWZ0RWxlbWVudHMobisxLGEtMSwtMSksaS5saW5lcy5zZXQobyxyLmNsb25lKCkpfXRoaXMuaXNVc2VyU2Nyb2xsaW5nfHwoaS55ZGlzcD1pLnliYXNlKSx0aGlzLl9vblNjcm9sbC5maXJlKGkueWRpc3ApfSxyLnByb3RvdHlwZS5zY3JvbGxMaW5lcz1mdW5jdGlvbihlLHQscil7dmFyIGk9dGhpcy5idWZmZXI7aWYoZTwwKXtpZigwPT09aS55ZGlzcClyZXR1cm47dGhpcy5pc1VzZXJTY3JvbGxpbmc9ITB9ZWxzZSBlK2kueWRpc3A+PWkueWJhc2UmJih0aGlzLmlzVXNlclNjcm9sbGluZz0hMSk7dmFyIG49aS55ZGlzcDtpLnlkaXNwPU1hdGgubWF4KE1hdGgubWluKGkueWRpc3ArZSxpLnliYXNlKSwwKSxuIT09aS55ZGlzcCYmKHR8fHRoaXMuX29uU2Nyb2xsLmZpcmUoaS55ZGlzcCkpfSxyLnByb3RvdHlwZS5zY3JvbGxQYWdlcz1mdW5jdGlvbihlKXt0aGlzLnNjcm9sbExpbmVzKGUqKHRoaXMucm93cy0xKSl9LHIucHJvdG90eXBlLnNjcm9sbFRvVG9wPWZ1bmN0aW9uKCl7dGhpcy5zY3JvbGxMaW5lcygtdGhpcy5idWZmZXIueWRpc3ApfSxyLnByb3RvdHlwZS5zY3JvbGxUb0JvdHRvbT1mdW5jdGlvbigpe3RoaXMuc2Nyb2xsTGluZXModGhpcy5idWZmZXIueWJhc2UtdGhpcy5idWZmZXIueWRpc3ApfSxyLnByb3RvdHlwZS5zY3JvbGxUb0xpbmU9ZnVuY3Rpb24oZSl7dmFyIHQ9ZS10aGlzLmJ1ZmZlci55ZGlzcDswIT09dCYmdGhpcy5zY3JvbGxMaW5lcyh0KX0sbyhbcygwLGEuSU9wdGlvbnNTZXJ2aWNlKV0scil9KHUuRGlzcG9zYWJsZSk7dC5CdWZmZXJTZXJ2aWNlPWh9LDc5OTQ6KGUsdCk9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5DaGFyc2V0U2VydmljZT12b2lkIDA7dmFyIHI9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKCl7dGhpcy5nbGV2ZWw9MCx0aGlzLl9jaGFyc2V0cz1bXX1yZXR1cm4gZS5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLmNoYXJzZXQ9dm9pZCAwLHRoaXMuX2NoYXJzZXRzPVtdLHRoaXMuZ2xldmVsPTB9LGUucHJvdG90eXBlLnNldGdMZXZlbD1mdW5jdGlvbihlKXt0aGlzLmdsZXZlbD1lLHRoaXMuY2hhcnNldD10aGlzLl9jaGFyc2V0c1tlXX0sZS5wcm90b3R5cGUuc2V0Z0NoYXJzZXQ9ZnVuY3Rpb24oZSx0KXt0aGlzLl9jaGFyc2V0c1tlXT10LHRoaXMuZ2xldmVsPT09ZSYmKHRoaXMuY2hhcnNldD10KX0sZX0oKTt0LkNoYXJzZXRTZXJ2aWNlPXJ9LDE3NTM6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Db3JlTW91c2VTZXJ2aWNlPXZvaWQgMDt2YXIgbz1yKDI1ODUpLHM9cig4NDYwKSxhPXtOT05FOntldmVudHM6MCxyZXN0cmljdDpmdW5jdGlvbigpe3JldHVybiExfX0sWDEwOntldmVudHM6MSxyZXN0cmljdDpmdW5jdGlvbihlKXtyZXR1cm4gNCE9PWUuYnV0dG9uJiYxPT09ZS5hY3Rpb24mJihlLmN0cmw9ITEsZS5hbHQ9ITEsZS5zaGlmdD0hMSwhMCl9fSxWVDIwMDp7ZXZlbnRzOjE5LHJlc3RyaWN0OmZ1bmN0aW9uKGUpe3JldHVybiAzMiE9PWUuYWN0aW9ufX0sRFJBRzp7ZXZlbnRzOjIzLHJlc3RyaWN0OmZ1bmN0aW9uKGUpe3JldHVybiAzMiE9PWUuYWN0aW9ufHwzIT09ZS5idXR0b259fSxBTlk6e2V2ZW50czozMSxyZXN0cmljdDpmdW5jdGlvbihlKXtyZXR1cm4hMH19fTtmdW5jdGlvbiBjKGUsdCl7dmFyIHI9KGUuY3RybD8xNjowKXwoZS5zaGlmdD80OjApfChlLmFsdD84OjApO3JldHVybiA0PT09ZS5idXR0b24/KHJ8PTY0LHJ8PWUuYWN0aW9uKToocnw9MyZlLmJ1dHRvbiw0JmUuYnV0dG9uJiYocnw9NjQpLDgmZS5idXR0b24mJihyfD0xMjgpLDMyPT09ZS5hY3Rpb24/cnw9MzI6MCE9PWUuYWN0aW9ufHx0fHwocnw9MykpLHJ9dmFyIGw9U3RyaW5nLmZyb21DaGFyQ29kZSx1PXtERUZBVUxUOmZ1bmN0aW9uKGUpe3ZhciB0PVtjKGUsITEpKzMyLGUuY29sKzMyLGUucm93KzMyXTtyZXR1cm4gdFswXT4yNTV8fHRbMV0+MjU1fHx0WzJdPjI1NT8iIjoiG1tNIitsKHRbMF0pK2wodFsxXSkrbCh0WzJdKX0sU0dSOmZ1bmN0aW9uKGUpe3ZhciB0PTA9PT1lLmFjdGlvbiYmNCE9PWUuYnV0dG9uPyJtIjoiTSI7cmV0dXJuIhtbPCIrYyhlLCEwKSsiOyIrZS5jb2wrIjsiK2Uucm93K3R9fSxoPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlLHQpe3RoaXMuX2J1ZmZlclNlcnZpY2U9ZSx0aGlzLl9jb3JlU2VydmljZT10LHRoaXMuX3Byb3RvY29scz17fSx0aGlzLl9lbmNvZGluZ3M9e30sdGhpcy5fYWN0aXZlUHJvdG9jb2w9IiIsdGhpcy5fYWN0aXZlRW5jb2Rpbmc9IiIsdGhpcy5fb25Qcm90b2NvbENoYW5nZT1uZXcgcy5FdmVudEVtaXR0ZXIsdGhpcy5fbGFzdEV2ZW50PW51bGw7Zm9yKHZhciByPTAsaT1PYmplY3Qua2V5cyhhKTtyPGkubGVuZ3RoO3IrKyl7dmFyIG49aVtyXTt0aGlzLmFkZFByb3RvY29sKG4sYVtuXSl9Zm9yKHZhciBvPTAsYz1PYmplY3Qua2V5cyh1KTtvPGMubGVuZ3RoO28rKyl7dmFyIGw9Y1tvXTt0aGlzLmFkZEVuY29kaW5nKGwsdVtsXSl9dGhpcy5yZXNldCgpfXJldHVybiBlLnByb3RvdHlwZS5hZGRQcm90b2NvbD1mdW5jdGlvbihlLHQpe3RoaXMuX3Byb3RvY29sc1tlXT10fSxlLnByb3RvdHlwZS5hZGRFbmNvZGluZz1mdW5jdGlvbihlLHQpe3RoaXMuX2VuY29kaW5nc1tlXT10fSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImFjdGl2ZVByb3RvY29sIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2FjdGl2ZVByb3RvY29sfSxzZXQ6ZnVuY3Rpb24oZSl7aWYoIXRoaXMuX3Byb3RvY29sc1tlXSl0aHJvdyBuZXcgRXJyb3IoJ3Vua25vd24gcHJvdG9jb2wgIicrZSsnIicpO3RoaXMuX2FjdGl2ZVByb3RvY29sPWUsdGhpcy5fb25Qcm90b2NvbENoYW5nZS5maXJlKHRoaXMuX3Byb3RvY29sc1tlXS5ldmVudHMpfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiYXJlTW91c2VFdmVudHNBY3RpdmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gMCE9PXRoaXMuX3Byb3RvY29sc1t0aGlzLl9hY3RpdmVQcm90b2NvbF0uZXZlbnRzfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiYWN0aXZlRW5jb2RpbmciLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYWN0aXZlRW5jb2Rpbmd9LHNldDpmdW5jdGlvbihlKXtpZighdGhpcy5fZW5jb2RpbmdzW2VdKXRocm93IG5ldyBFcnJvcigndW5rbm93biBlbmNvZGluZyAiJytlKyciJyk7dGhpcy5fYWN0aXZlRW5jb2Rpbmc9ZX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5yZXNldD1mdW5jdGlvbigpe3RoaXMuYWN0aXZlUHJvdG9jb2w9Ik5PTkUiLHRoaXMuYWN0aXZlRW5jb2Rpbmc9IkRFRkFVTFQiLHRoaXMuX2xhc3RFdmVudD1udWxsfSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uUHJvdG9jb2xDaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25Qcm90b2NvbENoYW5nZS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS50cmlnZ2VyTW91c2VFdmVudD1mdW5jdGlvbihlKXtpZihlLmNvbDwwfHxlLmNvbD49dGhpcy5fYnVmZmVyU2VydmljZS5jb2xzfHxlLnJvdzwwfHxlLnJvdz49dGhpcy5fYnVmZmVyU2VydmljZS5yb3dzKXJldHVybiExO2lmKDQ9PT1lLmJ1dHRvbiYmMzI9PT1lLmFjdGlvbilyZXR1cm4hMTtpZigzPT09ZS5idXR0b24mJjMyIT09ZS5hY3Rpb24pcmV0dXJuITE7aWYoNCE9PWUuYnV0dG9uJiYoMj09PWUuYWN0aW9ufHwzPT09ZS5hY3Rpb24pKXJldHVybiExO2lmKGUuY29sKyssZS5yb3crKywzMj09PWUuYWN0aW9uJiZ0aGlzLl9sYXN0RXZlbnQmJnRoaXMuX2NvbXBhcmVFdmVudHModGhpcy5fbGFzdEV2ZW50LGUpKXJldHVybiExO2lmKCF0aGlzLl9wcm90b2NvbHNbdGhpcy5fYWN0aXZlUHJvdG9jb2xdLnJlc3RyaWN0KGUpKXJldHVybiExO3ZhciB0PXRoaXMuX2VuY29kaW5nc1t0aGlzLl9hY3RpdmVFbmNvZGluZ10oZSk7cmV0dXJuIHQmJigiREVGQVVMVCI9PT10aGlzLl9hY3RpdmVFbmNvZGluZz90aGlzLl9jb3JlU2VydmljZS50cmlnZ2VyQmluYXJ5RXZlbnQodCk6dGhpcy5fY29yZVNlcnZpY2UudHJpZ2dlckRhdGFFdmVudCh0LCEwKSksdGhpcy5fbGFzdEV2ZW50PWUsITB9LGUucHJvdG90eXBlLmV4cGxhaW5FdmVudHM9ZnVuY3Rpb24oZSl7cmV0dXJue2Rvd246ISEoMSZlKSx1cDohISgyJmUpLGRyYWc6ISEoNCZlKSxtb3ZlOiEhKDgmZSksd2hlZWw6ISEoMTYmZSl9fSxlLnByb3RvdHlwZS5fY29tcGFyZUV2ZW50cz1mdW5jdGlvbihlLHQpe3JldHVybiBlLmNvbD09PXQuY29sJiZlLnJvdz09PXQucm93JiZlLmJ1dHRvbj09PXQuYnV0dG9uJiZlLmFjdGlvbj09PXQuYWN0aW9uJiZlLmN0cmw9PT10LmN0cmwmJmUuYWx0PT09dC5hbHQmJmUuc2hpZnQ9PT10LnNoaWZ0fSxpKFtuKDAsby5JQnVmZmVyU2VydmljZSksbigxLG8uSUNvcmVTZXJ2aWNlKV0sZSl9KCk7dC5Db3JlTW91c2VTZXJ2aWNlPWh9LDY5NzU6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpLG49dGhpcyYmdGhpcy5fX2V4dGVuZHN8fChpPWZ1bmN0aW9uKGUsdCl7cmV0dXJuIGk9T2JqZWN0LnNldFByb3RvdHlwZU9mfHx7X19wcm90b19fOltdfWluc3RhbmNlb2YgQXJyYXkmJmZ1bmN0aW9uKGUsdCl7ZS5fX3Byb3RvX189dH18fGZ1bmN0aW9uKGUsdCl7Zm9yKHZhciByIGluIHQpT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKHQscikmJihlW3JdPXRbcl0pfSxpKGUsdCl9LGZ1bmN0aW9uKGUsdCl7aWYoImZ1bmN0aW9uIiE9dHlwZW9mIHQmJm51bGwhPT10KXRocm93IG5ldyBUeXBlRXJyb3IoIkNsYXNzIGV4dGVuZHMgdmFsdWUgIitTdHJpbmcodCkrIiBpcyBub3QgYSBjb25zdHJ1Y3RvciBvciBudWxsIik7ZnVuY3Rpb24gcigpe3RoaXMuY29uc3RydWN0b3I9ZX1pKGUsdCksZS5wcm90b3R5cGU9bnVsbD09PXQ/T2JqZWN0LmNyZWF0ZSh0KTooci5wcm90b3R5cGU9dC5wcm90b3R5cGUsbmV3IHIpfSksbz10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LHM9dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX07T2JqZWN0LmRlZmluZVByb3BlcnR5KHQsIl9fZXNNb2R1bGUiLHt2YWx1ZTohMH0pLHQuQ29yZVNlcnZpY2U9dm9pZCAwO3ZhciBhPXIoMjU4NSksYz1yKDg0NjApLGw9cigxNDM5KSx1PXIoODQ0KSxoPU9iamVjdC5mcmVlemUoe2luc2VydE1vZGU6ITF9KSxmPU9iamVjdC5mcmVlemUoe2FwcGxpY2F0aW9uQ3Vyc29yS2V5czohMSxhcHBsaWNhdGlvbktleXBhZDohMSxicmFja2V0ZWRQYXN0ZU1vZGU6ITEsb3JpZ2luOiExLHJldmVyc2VXcmFwYXJvdW5kOiExLHNlbmRGb2N1czohMSx3cmFwYXJvdW5kOiEwfSksXz1mdW5jdGlvbihlKXtmdW5jdGlvbiB0KHQscixpLG4pe3ZhciBvPWUuY2FsbCh0aGlzKXx8dGhpcztyZXR1cm4gby5fYnVmZmVyU2VydmljZT1yLG8uX2xvZ1NlcnZpY2U9aSxvLl9vcHRpb25zU2VydmljZT1uLG8uaXNDdXJzb3JJbml0aWFsaXplZD0hMSxvLmlzQ3Vyc29ySGlkZGVuPSExLG8uX29uRGF0YT1vLnJlZ2lzdGVyKG5ldyBjLkV2ZW50RW1pdHRlciksby5fb25Vc2VySW5wdXQ9by5yZWdpc3RlcihuZXcgYy5FdmVudEVtaXR0ZXIpLG8uX29uQmluYXJ5PW8ucmVnaXN0ZXIobmV3IGMuRXZlbnRFbWl0dGVyKSxvLl9zY3JvbGxUb0JvdHRvbT10LG8ucmVnaXN0ZXIoe2Rpc3Bvc2U6ZnVuY3Rpb24oKXtyZXR1cm4gby5fc2Nyb2xsVG9Cb3R0b209dm9pZCAwfX0pLG8ubW9kZXM9KDAsbC5jbG9uZSkoaCksby5kZWNQcml2YXRlTW9kZXM9KDAsbC5jbG9uZSkoZiksb31yZXR1cm4gbih0LGUpLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25EYXRhIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uRGF0YS5ldmVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkodC5wcm90b3R5cGUsIm9uVXNlcklucHV0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uVXNlcklucHV0LmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LnByb3RvdHlwZSwib25CaW5hcnkiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25CaW5hcnkuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksdC5wcm90b3R5cGUucmVzZXQ9ZnVuY3Rpb24oKXt0aGlzLm1vZGVzPSgwLGwuY2xvbmUpKGgpLHRoaXMuZGVjUHJpdmF0ZU1vZGVzPSgwLGwuY2xvbmUpKGYpfSx0LnByb3RvdHlwZS50cmlnZ2VyRGF0YUV2ZW50PWZ1bmN0aW9uKGUsdCl7aWYodm9pZCAwPT09dCYmKHQ9ITEpLCF0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmRpc2FibGVTdGRpbil7dmFyIHI9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXI7ci55YmFzZSE9PXIueWRpc3AmJnRoaXMuX3Njcm9sbFRvQm90dG9tKCksdCYmdGhpcy5fb25Vc2VySW5wdXQuZmlyZSgpLHRoaXMuX2xvZ1NlcnZpY2UuZGVidWcoJ3NlbmRpbmcgZGF0YSAiJytlKyciJywoZnVuY3Rpb24oKXtyZXR1cm4gZS5zcGxpdCgiIikubWFwKChmdW5jdGlvbihlKXtyZXR1cm4gZS5jaGFyQ29kZUF0KDApfSkpfSkpLHRoaXMuX29uRGF0YS5maXJlKGUpfX0sdC5wcm90b3R5cGUudHJpZ2dlckJpbmFyeUV2ZW50PWZ1bmN0aW9uKGUpe3RoaXMuX29wdGlvbnNTZXJ2aWNlLm9wdGlvbnMuZGlzYWJsZVN0ZGlufHwodGhpcy5fbG9nU2VydmljZS5kZWJ1Zygnc2VuZGluZyBiaW5hcnkgIicrZSsnIicsKGZ1bmN0aW9uKCl7cmV0dXJuIGUuc3BsaXQoIiIpLm1hcCgoZnVuY3Rpb24oZSl7cmV0dXJuIGUuY2hhckNvZGVBdCgwKX0pKX0pKSx0aGlzLl9vbkJpbmFyeS5maXJlKGUpKX0sbyhbcygxLGEuSUJ1ZmZlclNlcnZpY2UpLHMoMixhLklMb2dTZXJ2aWNlKSxzKDMsYS5JT3B0aW9uc1NlcnZpY2UpXSx0KX0odS5EaXNwb3NhYmxlKTt0LkNvcmVTZXJ2aWNlPV99LDM3MzA6ZnVuY3Rpb24oZSx0LHIpe3ZhciBpPXRoaXMmJnRoaXMuX19kZWNvcmF0ZXx8ZnVuY3Rpb24oZSx0LHIsaSl7dmFyIG4sbz1hcmd1bWVudHMubGVuZ3RoLHM9bzwzP3Q6bnVsbD09PWk/aT1PYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHQscik6aTtpZigib2JqZWN0Ij09dHlwZW9mIFJlZmxlY3QmJiJmdW5jdGlvbiI9PXR5cGVvZiBSZWZsZWN0LmRlY29yYXRlKXM9UmVmbGVjdC5kZWNvcmF0ZShlLHQscixpKTtlbHNlIGZvcih2YXIgYT1lLmxlbmd0aC0xO2E+PTA7YS0tKShuPWVbYV0pJiYocz0obzwzP24ocyk6bz4zP24odCxyLHMpOm4odCxyKSl8fHMpO3JldHVybiBvPjMmJnMmJk9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LHIscyksc30sbj10aGlzJiZ0aGlzLl9fcGFyYW18fGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGZ1bmN0aW9uKHIsaSl7dChyLGksZSl9fTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5EaXJ0eVJvd1NlcnZpY2U9dm9pZCAwO3ZhciBvPXIoMjU4NSkscz1mdW5jdGlvbigpe2Z1bmN0aW9uIGUoZSl7dGhpcy5fYnVmZmVyU2VydmljZT1lLHRoaXMuY2xlYXJSYW5nZSgpfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsInN0YXJ0Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX3N0YXJ0fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiZW5kIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2VuZH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5jbGVhclJhbmdlPWZ1bmN0aW9uKCl7dGhpcy5fc3RhcnQ9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueSx0aGlzLl9lbmQ9dGhpcy5fYnVmZmVyU2VydmljZS5idWZmZXIueX0sZS5wcm90b3R5cGUubWFya0RpcnR5PWZ1bmN0aW9uKGUpe2U8dGhpcy5fc3RhcnQ/dGhpcy5fc3RhcnQ9ZTplPnRoaXMuX2VuZCYmKHRoaXMuX2VuZD1lKX0sZS5wcm90b3R5cGUubWFya1JhbmdlRGlydHk9ZnVuY3Rpb24oZSx0KXtpZihlPnQpe3ZhciByPWU7ZT10LHQ9cn1lPHRoaXMuX3N0YXJ0JiYodGhpcy5fc3RhcnQ9ZSksdD50aGlzLl9lbmQmJih0aGlzLl9lbmQ9dCl9LGUucHJvdG90eXBlLm1hcmtBbGxEaXJ0eT1mdW5jdGlvbigpe3RoaXMubWFya1JhbmdlRGlydHkoMCx0aGlzLl9idWZmZXJTZXJ2aWNlLnJvd3MtMSl9LGkoW24oMCxvLklCdWZmZXJTZXJ2aWNlKV0sZSl9KCk7dC5EaXJ0eVJvd1NlcnZpY2U9c30sNDM0ODpmdW5jdGlvbihlLHQscil7dmFyIGk9dGhpcyYmdGhpcy5fX3NwcmVhZEFycmF5fHxmdW5jdGlvbihlLHQscil7aWYocnx8Mj09PWFyZ3VtZW50cy5sZW5ndGgpZm9yKHZhciBpLG49MCxvPXQubGVuZ3RoO248bztuKyspIWkmJm4gaW4gdHx8KGl8fChpPUFycmF5LnByb3RvdHlwZS5zbGljZS5jYWxsKHQsMCxuKSksaVtuXT10W25dKTtyZXR1cm4gZS5jb25jYXQoaXx8QXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwodCkpfTtPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5JbnN0YW50aWF0aW9uU2VydmljZT10LlNlcnZpY2VDb2xsZWN0aW9uPXZvaWQgMDt2YXIgbj1yKDI1ODUpLG89cig4MzQzKSxzPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe2Zvcih2YXIgZT1bXSx0PTA7dDxhcmd1bWVudHMubGVuZ3RoO3QrKyllW3RdPWFyZ3VtZW50c1t0XTt0aGlzLl9lbnRyaWVzPW5ldyBNYXA7Zm9yKHZhciByPTAsaT1lO3I8aS5sZW5ndGg7cisrKXt2YXIgbj1pW3JdLG89blswXSxzPW5bMV07dGhpcy5zZXQobyxzKX19cmV0dXJuIGUucHJvdG90eXBlLnNldD1mdW5jdGlvbihlLHQpe3ZhciByPXRoaXMuX2VudHJpZXMuZ2V0KGUpO3JldHVybiB0aGlzLl9lbnRyaWVzLnNldChlLHQpLHJ9LGUucHJvdG90eXBlLmZvckVhY2g9ZnVuY3Rpb24oZSl7dGhpcy5fZW50cmllcy5mb3JFYWNoKChmdW5jdGlvbih0LHIpe3JldHVybiBlKHIsdCl9KSl9LGUucHJvdG90eXBlLmhhcz1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fZW50cmllcy5oYXMoZSl9LGUucHJvdG90eXBlLmdldD1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fZW50cmllcy5nZXQoZSl9LGV9KCk7dC5TZXJ2aWNlQ29sbGVjdGlvbj1zO3ZhciBhPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX3NlcnZpY2VzPW5ldyBzLHRoaXMuX3NlcnZpY2VzLnNldChuLklJbnN0YW50aWF0aW9uU2VydmljZSx0aGlzKX1yZXR1cm4gZS5wcm90b3R5cGUuc2V0U2VydmljZT1mdW5jdGlvbihlLHQpe3RoaXMuX3NlcnZpY2VzLnNldChlLHQpfSxlLnByb3RvdHlwZS5nZXRTZXJ2aWNlPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9zZXJ2aWNlcy5nZXQoZSl9LGUucHJvdG90eXBlLmNyZWF0ZUluc3RhbmNlPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1bXSxyPTE7cjxhcmd1bWVudHMubGVuZ3RoO3IrKyl0W3ItMV09YXJndW1lbnRzW3JdO2Zvcih2YXIgbj0oMCxvLmdldFNlcnZpY2VEZXBlbmRlbmNpZXMpKGUpLnNvcnQoKGZ1bmN0aW9uKGUsdCl7cmV0dXJuIGUuaW5kZXgtdC5pbmRleH0pKSxzPVtdLGE9MCxjPW47YTxjLmxlbmd0aDthKyspe3ZhciBsPWNbYV0sdT10aGlzLl9zZXJ2aWNlcy5nZXQobC5pZCk7aWYoIXUpdGhyb3cgbmV3IEVycm9yKCJbY3JlYXRlSW5zdGFuY2VdICIrZS5uYW1lKyIgZGVwZW5kcyBvbiBVTktOT1dOIHNlcnZpY2UgIitsLmlkKyIuIik7cy5wdXNoKHUpfXZhciBoPW4ubGVuZ3RoPjA/blswXS5pbmRleDp0Lmxlbmd0aDtpZih0Lmxlbmd0aCE9PWgpdGhyb3cgbmV3IEVycm9yKCJbY3JlYXRlSW5zdGFuY2VdIEZpcnN0IHNlcnZpY2UgZGVwZW5kZW5jeSBvZiAiK2UubmFtZSsiIGF0IHBvc2l0aW9uICIrKGgrMSkrIiBjb25mbGljdHMgd2l0aCAiK3QubGVuZ3RoKyIgc3RhdGljIGFyZ3VtZW50cyIpO3JldHVybiBuZXcoZS5iaW5kLmFwcGx5KGUsaShbdm9pZCAwXSxpKGkoW10sdCwhMCkscywhMCksITEpKSl9LGV9KCk7dC5JbnN0YW50aWF0aW9uU2VydmljZT1hfSw3ODY2OmZ1bmN0aW9uKGUsdCxyKXt2YXIgaT10aGlzJiZ0aGlzLl9fZGVjb3JhdGV8fGZ1bmN0aW9uKGUsdCxyLGkpe3ZhciBuLG89YXJndW1lbnRzLmxlbmd0aCxzPW88Mz90Om51bGw9PT1pP2k9T2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcih0LHIpOmk7aWYoIm9iamVjdCI9PXR5cGVvZiBSZWZsZWN0JiYiZnVuY3Rpb24iPT10eXBlb2YgUmVmbGVjdC5kZWNvcmF0ZSlzPVJlZmxlY3QuZGVjb3JhdGUoZSx0LHIsaSk7ZWxzZSBmb3IodmFyIGE9ZS5sZW5ndGgtMTthPj0wO2EtLSkobj1lW2FdKSYmKHM9KG88Mz9uKHMpOm8+Mz9uKHQscixzKTpuKHQscikpfHxzKTtyZXR1cm4gbz4zJiZzJiZPYmplY3QuZGVmaW5lUHJvcGVydHkodCxyLHMpLHN9LG49dGhpcyYmdGhpcy5fX3BhcmFtfHxmdW5jdGlvbihlLHQpe3JldHVybiBmdW5jdGlvbihyLGkpe3QocixpLGUpfX0sbz10aGlzJiZ0aGlzLl9fc3ByZWFkQXJyYXl8fGZ1bmN0aW9uKGUsdCxyKXtpZihyfHwyPT09YXJndW1lbnRzLmxlbmd0aClmb3IodmFyIGksbj0wLG89dC5sZW5ndGg7bjxvO24rKykhaSYmbiBpbiB0fHwoaXx8KGk9QXJyYXkucHJvdG90eXBlLnNsaWNlLmNhbGwodCwwLG4pKSxpW25dPXRbbl0pO3JldHVybiBlLmNvbmNhdChpfHxBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCh0KSl9O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LkxvZ1NlcnZpY2U9dm9pZCAwO3ZhciBzPXIoMjU4NSksYT17ZGVidWc6cy5Mb2dMZXZlbEVudW0uREVCVUcsaW5mbzpzLkxvZ0xldmVsRW51bS5JTkZPLHdhcm46cy5Mb2dMZXZlbEVudW0uV0FSTixlcnJvcjpzLkxvZ0xldmVsRW51bS5FUlJPUixvZmY6cy5Mb2dMZXZlbEVudW0uT0ZGfSxjPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZShlKXt2YXIgdD10aGlzO3RoaXMuX29wdGlvbnNTZXJ2aWNlPWUsdGhpcy5sb2dMZXZlbD1zLkxvZ0xldmVsRW51bS5PRkYsdGhpcy5fdXBkYXRlTG9nTGV2ZWwoKSx0aGlzLl9vcHRpb25zU2VydmljZS5vbk9wdGlvbkNoYW5nZSgoZnVuY3Rpb24oZSl7ImxvZ0xldmVsIj09PWUmJnQuX3VwZGF0ZUxvZ0xldmVsKCl9KSl9cmV0dXJuIGUucHJvdG90eXBlLl91cGRhdGVMb2dMZXZlbD1mdW5jdGlvbigpe3RoaXMubG9nTGV2ZWw9YVt0aGlzLl9vcHRpb25zU2VydmljZS5vcHRpb25zLmxvZ0xldmVsXX0sZS5wcm90b3R5cGUuX2V2YWxMYXp5T3B0aW9uYWxQYXJhbXM9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PTA7dDxlLmxlbmd0aDt0KyspImZ1bmN0aW9uIj09dHlwZW9mIGVbdF0mJihlW3RdPWVbdF0oKSl9LGUucHJvdG90eXBlLl9sb2c9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX2V2YWxMYXp5T3B0aW9uYWxQYXJhbXMociksZS5jYWxsLmFwcGx5KGUsbyhbY29uc29sZSwieHRlcm0uanM6ICIrdF0sciwhMSkpfSxlLnByb3RvdHlwZS5kZWJ1Zz1mdW5jdGlvbihlKXtmb3IodmFyIHQ9W10scj0xO3I8YXJndW1lbnRzLmxlbmd0aDtyKyspdFtyLTFdPWFyZ3VtZW50c1tyXTt0aGlzLmxvZ0xldmVsPD1zLkxvZ0xldmVsRW51bS5ERUJVRyYmdGhpcy5fbG9nKGNvbnNvbGUubG9nLGUsdCl9LGUucHJvdG90eXBlLmluZm89ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PVtdLHI9MTtyPGFyZ3VtZW50cy5sZW5ndGg7cisrKXRbci0xXT1hcmd1bWVudHNbcl07dGhpcy5sb2dMZXZlbDw9cy5Mb2dMZXZlbEVudW0uSU5GTyYmdGhpcy5fbG9nKGNvbnNvbGUuaW5mbyxlLHQpfSxlLnByb3RvdHlwZS53YXJuPWZ1bmN0aW9uKGUpe2Zvcih2YXIgdD1bXSxyPTE7cjxhcmd1bWVudHMubGVuZ3RoO3IrKyl0W3ItMV09YXJndW1lbnRzW3JdO3RoaXMubG9nTGV2ZWw8PXMuTG9nTGV2ZWxFbnVtLldBUk4mJnRoaXMuX2xvZyhjb25zb2xlLndhcm4sZSx0KX0sZS5wcm90b3R5cGUuZXJyb3I9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PVtdLHI9MTtyPGFyZ3VtZW50cy5sZW5ndGg7cisrKXRbci0xXT1hcmd1bWVudHNbcl07dGhpcy5sb2dMZXZlbDw9cy5Mb2dMZXZlbEVudW0uRVJST1ImJnRoaXMuX2xvZyhjb25zb2xlLmVycm9yLGUsdCl9LGkoW24oMCxzLklPcHRpb25zU2VydmljZSldLGUpfSgpO3QuTG9nU2VydmljZT1jfSw3MzAyOmZ1bmN0aW9uKGUsdCxyKXt2YXIgaT10aGlzJiZ0aGlzLl9fYXNzaWdufHxmdW5jdGlvbigpe3JldHVybiBpPU9iamVjdC5hc3NpZ258fGZ1bmN0aW9uKGUpe2Zvcih2YXIgdCxyPTEsaT1hcmd1bWVudHMubGVuZ3RoO3I8aTtyKyspZm9yKHZhciBuIGluIHQ9YXJndW1lbnRzW3JdKU9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbCh0LG4pJiYoZVtuXT10W25dKTtyZXR1cm4gZX0saS5hcHBseSh0aGlzLGFyZ3VtZW50cyl9O09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0Lk9wdGlvbnNTZXJ2aWNlPXQuREVGQVVMVF9PUFRJT05TPXQuREVGQVVMVF9CRUxMX1NPVU5EPXZvaWQgMDt2YXIgbj1yKDg0NjApLG89cig2MTE0KTt0LkRFRkFVTFRfQkVMTF9TT1VORD0iZGF0YTphdWRpby9tcDM7YmFzZTY0LFNVUXpCQUFBQUFBQUkxUlRVMFVBQUFBUEFBQURUR0YyWmpVNExqTXlMakV3TkFBQUFBQUFBQUFBQUFBQS8vdFF4QUFEQjhBaFNteGhJSUVWQ1NpSnJEQ1FCVGN1M1VyQUl3VWRrUmdRYkZBWkMxQ1FFd1RKOW1qUnZCQTRVT0xEOG5LVk9XZmgrVWxLM3ovMTc3T1hyZk9kS2w3cHluM1hmLy9XcmV5VFJVb0FXZ0Jna09BR2JaSEJnRzFPRjZ6TTgyRFdiWmFVbU1CcHRnUWhHanN5WXFjOWFlOVhGejI4MDk0OE5NQldJbmxqeXpzTlJGTFBXZG5aR1dyZGREc2pLMXVudVNyVk45akpzSzhLdVF0UUN0TUJqQ0V0SW1JU2ROS0pPb3BJcEJGcE5TTWJJSENTUnBSUjVpYWtqVGl5ekxoY2hVVUJ3Q2d5S2l3ZUJ2LzdVc1FiZzhpc1ZOb01QTWpBQUFBMGdBQUFCRVZGR21ncUsvLy8vOWJQLzZYQ3lreEJUVVV6TGpFd01LcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXEiLHQuREVGQVVMVF9PUFRJT05TPXtjb2xzOjgwLHJvd3M6MjQsY3Vyc29yQmxpbms6ITEsY3Vyc29yU3R5bGU6ImJsb2NrIixjdXJzb3JXaWR0aDoxLGN1c3RvbUdseXBoczohMCxiZWxsU291bmQ6dC5ERUZBVUxUX0JFTExfU09VTkQsYmVsbFN0eWxlOiJub25lIixkcmF3Qm9sZFRleHRJbkJyaWdodENvbG9yczohMCxmYXN0U2Nyb2xsTW9kaWZpZXI6ImFsdCIsZmFzdFNjcm9sbFNlbnNpdGl2aXR5OjUsZm9udEZhbWlseToiY291cmllci1uZXcsIGNvdXJpZXIsIG1vbm9zcGFjZSIsZm9udFNpemU6MTUsZm9udFdlaWdodDoibm9ybWFsIixmb250V2VpZ2h0Qm9sZDoiYm9sZCIsbGluZUhlaWdodDoxLGxpbmtUb29sdGlwSG92ZXJEdXJhdGlvbjo1MDAsbGV0dGVyU3BhY2luZzowLGxvZ0xldmVsOiJpbmZvIixzY3JvbGxiYWNrOjFlMyxzY3JvbGxTZW5zaXRpdml0eToxLHNjcmVlblJlYWRlck1vZGU6ITEsbWFjT3B0aW9uSXNNZXRhOiExLG1hY09wdGlvbkNsaWNrRm9yY2VzU2VsZWN0aW9uOiExLG1pbmltdW1Db250cmFzdFJhdGlvOjEsZGlzYWJsZVN0ZGluOiExLGFsbG93UHJvcG9zZWRBcGk6ITAsYWxsb3dUcmFuc3BhcmVuY3k6ITEsdGFiU3RvcFdpZHRoOjgsdGhlbWU6e30scmlnaHRDbGlja1NlbGVjdHNXb3JkOm8uaXNNYWMscmVuZGVyZXJUeXBlOiJjYW52YXMiLHdpbmRvd09wdGlvbnM6e30sd2luZG93c01vZGU6ITEsd29yZFNlcGFyYXRvcjoiICgpW117fScsXCJgIixhbHRDbGlja01vdmVzQ3Vyc29yOiEwLGNvbnZlcnRFb2w6ITEsdGVybU5hbWU6Inh0ZXJtIixjYW5jZWxFdmVudHM6ITF9O3ZhciBzPVsibm9ybWFsIiwiYm9sZCIsIjEwMCIsIjIwMCIsIjMwMCIsIjQwMCIsIjUwMCIsIjYwMCIsIjcwMCIsIjgwMCIsIjkwMCJdLGE9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe2Zvcih2YXIgciBpbiB0aGlzLl9vbk9wdGlvbkNoYW5nZT1uZXcgbi5FdmVudEVtaXR0ZXIsdGhpcy5fb3B0aW9ucz1pKHt9LHQuREVGQVVMVF9PUFRJT05TKSxlKWlmKHIgaW4gdGhpcy5fb3B0aW9ucyl0cnl7dmFyIG89ZVtyXTt0aGlzLl9vcHRpb25zW3JdPXRoaXMuX3Nhbml0aXplQW5kVmFsaWRhdGVPcHRpb24ocixvKX1jYXRjaChlKXtjb25zb2xlLmVycm9yKGUpfXRoaXMub3B0aW9ucz10aGlzLl9zZXR1cE9wdGlvbnModGhpcy5fb3B0aW9ucyl9cmV0dXJuIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25PcHRpb25DaGFuZ2UiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fb25PcHRpb25DaGFuZ2UuZXZlbnR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUuX3NldHVwT3B0aW9ucz1mdW5jdGlvbihlKXt2YXIgcj10aGlzLG49aSh7fSxlKSxvPWZ1bmN0aW9uKGUpe09iamVjdC5kZWZpbmVQcm9wZXJ0eShuLGUse2dldDpmdW5jdGlvbigpe2lmKCEoZSBpbiB0LkRFRkFVTFRfT1BUSU9OUykpdGhyb3cgbmV3IEVycm9yKCdObyBvcHRpb24gd2l0aCBrZXkgIicrZSsnIicpO3JldHVybiByLl9vcHRpb25zW2VdfSxzZXQ6ZnVuY3Rpb24oaSl7aWYoIShlIGluIHQuREVGQVVMVF9PUFRJT05TKSl0aHJvdyBuZXcgRXJyb3IoJ05vIG9wdGlvbiB3aXRoIGtleSAiJytlKyciJyk7aT1yLl9zYW5pdGl6ZUFuZFZhbGlkYXRlT3B0aW9uKGUsaSksci5fb3B0aW9uc1tlXSE9PWkmJihyLl9vcHRpb25zW2VdPWksci5fb25PcHRpb25DaGFuZ2UuZmlyZShlKSl9fSl9O2Zvcih2YXIgcyBpbiBuKW8ocyk7cmV0dXJuIG59LGUucHJvdG90eXBlLnNldE9wdGlvbj1mdW5jdGlvbihlLHQpe3RoaXMub3B0aW9uc1tlXT10fSxlLnByb3RvdHlwZS5fc2FuaXRpemVBbmRWYWxpZGF0ZU9wdGlvbj1mdW5jdGlvbihlLHIpe3N3aXRjaChlKXtjYXNlImJlbGxTdHlsZSI6Y2FzZSJjdXJzb3JTdHlsZSI6Y2FzZSJyZW5kZXJlclR5cGUiOmNhc2Uid29yZFNlcGFyYXRvciI6cnx8KHI9dC5ERUZBVUxUX09QVElPTlNbZV0pO2JyZWFrO2Nhc2UiZm9udFdlaWdodCI6Y2FzZSJmb250V2VpZ2h0Qm9sZCI6aWYoIm51bWJlciI9PXR5cGVvZiByJiYxPD1yJiZyPD0xZTMpYnJlYWs7cj1zLmluY2x1ZGVzKHIpP3I6dC5ERUZBVUxUX09QVElPTlNbZV07YnJlYWs7Y2FzZSJjdXJzb3JXaWR0aCI6cj1NYXRoLmZsb29yKHIpO2Nhc2UibGluZUhlaWdodCI6Y2FzZSJ0YWJTdG9wV2lkdGgiOmlmKHI8MSl0aHJvdyBuZXcgRXJyb3IoZSsiIGNhbm5vdCBiZSBsZXNzIHRoYW4gMSwgdmFsdWU6ICIrcik7YnJlYWs7Y2FzZSJtaW5pbXVtQ29udHJhc3RSYXRpbyI6cj1NYXRoLm1heCgxLE1hdGgubWluKDIxLE1hdGgucm91bmQoMTAqcikvMTApKTticmVhaztjYXNlInNjcm9sbGJhY2siOmlmKChyPU1hdGgubWluKHIsNDI5NDk2NzI5NSkpPDApdGhyb3cgbmV3IEVycm9yKGUrIiBjYW5ub3QgYmUgbGVzcyB0aGFuIDAsIHZhbHVlOiAiK3IpO2JyZWFrO2Nhc2UiZmFzdFNjcm9sbFNlbnNpdGl2aXR5IjpjYXNlInNjcm9sbFNlbnNpdGl2aXR5IjppZihyPD0wKXRocm93IG5ldyBFcnJvcihlKyIgY2Fubm90IGJlIGxlc3MgdGhhbiBvciBlcXVhbCB0byAwLCB2YWx1ZTogIityKTtjYXNlInJvd3MiOmNhc2UiY29scyI6aWYoIXImJjAhPT1yKXRocm93IG5ldyBFcnJvcihlKyIgbXVzdCBiZSBudW1lcmljLCB2YWx1ZTogIityKX1yZXR1cm4gcn0sZS5wcm90b3R5cGUuZ2V0T3B0aW9uPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLm9wdGlvbnNbZV19LGV9KCk7dC5PcHRpb25zU2VydmljZT1hfSw4MzQzOihlLHQpPT57ZnVuY3Rpb24gcihlLHQscil7dC5kaSR0YXJnZXQ9PT10P3QuZGkkZGVwZW5kZW5jaWVzLnB1c2goe2lkOmUsaW5kZXg6cn0pOih0LmRpJGRlcGVuZGVuY2llcz1be2lkOmUsaW5kZXg6cn1dLHQuZGkkdGFyZ2V0PXQpfU9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LmNyZWF0ZURlY29yYXRvcj10LmdldFNlcnZpY2VEZXBlbmRlbmNpZXM9dC5zZXJ2aWNlUmVnaXN0cnk9dm9pZCAwLHQuc2VydmljZVJlZ2lzdHJ5PW5ldyBNYXAsdC5nZXRTZXJ2aWNlRGVwZW5kZW5jaWVzPWZ1bmN0aW9uKGUpe3JldHVybiBlLmRpJGRlcGVuZGVuY2llc3x8W119LHQuY3JlYXRlRGVjb3JhdG9yPWZ1bmN0aW9uKGUpe2lmKHQuc2VydmljZVJlZ2lzdHJ5LmhhcyhlKSlyZXR1cm4gdC5zZXJ2aWNlUmVnaXN0cnkuZ2V0KGUpO3ZhciBpPWZ1bmN0aW9uKGUsdCxuKXtpZigzIT09YXJndW1lbnRzLmxlbmd0aCl0aHJvdyBuZXcgRXJyb3IoIkBJU2VydmljZU5hbWUtZGVjb3JhdG9yIGNhbiBvbmx5IGJlIHVzZWQgdG8gZGVjb3JhdGUgYSBwYXJhbWV0ZXIiKTtyKGksZSxuKX07cmV0dXJuIGkudG9TdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm4gZX0sdC5zZXJ2aWNlUmVnaXN0cnkuc2V0KGUsaSksaX19LDI1ODU6KGUsdCxyKT0+e09iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0LklVbmljb2RlU2VydmljZT10LklPcHRpb25zU2VydmljZT10LklMb2dTZXJ2aWNlPXQuTG9nTGV2ZWxFbnVtPXQuSUluc3RhbnRpYXRpb25TZXJ2aWNlPXQuSURpcnR5Um93U2VydmljZT10LklDaGFyc2V0U2VydmljZT10LklDb3JlU2VydmljZT10LklDb3JlTW91c2VTZXJ2aWNlPXQuSUJ1ZmZlclNlcnZpY2U9dm9pZCAwO3ZhciBpLG49cig4MzQzKTt0LklCdWZmZXJTZXJ2aWNlPSgwLG4uY3JlYXRlRGVjb3JhdG9yKSgiQnVmZmVyU2VydmljZSIpLHQuSUNvcmVNb3VzZVNlcnZpY2U9KDAsbi5jcmVhdGVEZWNvcmF0b3IpKCJDb3JlTW91c2VTZXJ2aWNlIiksdC5JQ29yZVNlcnZpY2U9KDAsbi5jcmVhdGVEZWNvcmF0b3IpKCJDb3JlU2VydmljZSIpLHQuSUNoYXJzZXRTZXJ2aWNlPSgwLG4uY3JlYXRlRGVjb3JhdG9yKSgiQ2hhcnNldFNlcnZpY2UiKSx0LklEaXJ0eVJvd1NlcnZpY2U9KDAsbi5jcmVhdGVEZWNvcmF0b3IpKCJEaXJ0eVJvd1NlcnZpY2UiKSx0LklJbnN0YW50aWF0aW9uU2VydmljZT0oMCxuLmNyZWF0ZURlY29yYXRvcikoIkluc3RhbnRpYXRpb25TZXJ2aWNlIiksKGk9dC5Mb2dMZXZlbEVudW18fCh0LkxvZ0xldmVsRW51bT17fSkpW2kuREVCVUc9MF09IkRFQlVHIixpW2kuSU5GTz0xXT0iSU5GTyIsaVtpLldBUk49Ml09IldBUk4iLGlbaS5FUlJPUj0zXT0iRVJST1IiLGlbaS5PRkY9NF09Ik9GRiIsdC5JTG9nU2VydmljZT0oMCxuLmNyZWF0ZURlY29yYXRvcikoIkxvZ1NlcnZpY2UiKSx0LklPcHRpb25zU2VydmljZT0oMCxuLmNyZWF0ZURlY29yYXRvcikoIk9wdGlvbnNTZXJ2aWNlIiksdC5JVW5pY29kZVNlcnZpY2U9KDAsbi5jcmVhdGVEZWNvcmF0b3IpKCJVbmljb2RlU2VydmljZSIpfSwxNDgwOihlLHQscik9PntPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdC5Vbmljb2RlU2VydmljZT12b2lkIDA7dmFyIGk9cig4NDYwKSxuPXIoMjI1KSxvPWZ1bmN0aW9uKCl7ZnVuY3Rpb24gZSgpe3RoaXMuX3Byb3ZpZGVycz1PYmplY3QuY3JlYXRlKG51bGwpLHRoaXMuX2FjdGl2ZT0iIix0aGlzLl9vbkNoYW5nZT1uZXcgaS5FdmVudEVtaXR0ZXI7dmFyIGU9bmV3IG4uVW5pY29kZVY2O3RoaXMucmVnaXN0ZXIoZSksdGhpcy5fYWN0aXZlPWUudmVyc2lvbix0aGlzLl9hY3RpdmVQcm92aWRlcj1lfXJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uQ2hhbmdlIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX29uQ2hhbmdlLmV2ZW50fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwidmVyc2lvbnMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gT2JqZWN0LmtleXModGhpcy5fcHJvdmlkZXJzKX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImFjdGl2ZVZlcnNpb24iLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fYWN0aXZlfSxzZXQ6ZnVuY3Rpb24oZSl7aWYoIXRoaXMuX3Byb3ZpZGVyc1tlXSl0aHJvdyBuZXcgRXJyb3IoJ3Vua25vd24gVW5pY29kZSB2ZXJzaW9uICInK2UrJyInKTt0aGlzLl9hY3RpdmU9ZSx0aGlzLl9hY3RpdmVQcm92aWRlcj10aGlzLl9wcm92aWRlcnNbZV0sdGhpcy5fb25DaGFuZ2UuZmlyZShlKX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxlLnByb3RvdHlwZS5yZWdpc3Rlcj1mdW5jdGlvbihlKXt0aGlzLl9wcm92aWRlcnNbZS52ZXJzaW9uXT1lfSxlLnByb3RvdHlwZS53Y3dpZHRoPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9hY3RpdmVQcm92aWRlci53Y3dpZHRoKGUpfSxlLnByb3RvdHlwZS5nZXRTdHJpbmdDZWxsV2lkdGg9ZnVuY3Rpb24oZSl7Zm9yKHZhciB0PTAscj1lLmxlbmd0aCxpPTA7aTxyOysraSl7dmFyIG49ZS5jaGFyQ29kZUF0KGkpO2lmKDU1Mjk2PD1uJiZuPD01NjMxOSl7aWYoKytpPj1yKXJldHVybiB0K3RoaXMud2N3aWR0aChuKTt2YXIgbz1lLmNoYXJDb2RlQXQoaSk7NTYzMjA8PW8mJm88PTU3MzQzP249MTAyNCoobi01NTI5Nikrby01NjMyMCs2NTUzNjp0Kz10aGlzLndjd2lkdGgobyl9dCs9dGhpcy53Y3dpZHRoKG4pfXJldHVybiB0fSxlfSgpO3QuVW5pY29kZVNlcnZpY2U9b319LHQ9e307ZnVuY3Rpb24gcihpKXt2YXIgbj10W2ldO2lmKHZvaWQgMCE9PW4pcmV0dXJuIG4uZXhwb3J0czt2YXIgbz10W2ldPXtleHBvcnRzOnt9fTtyZXR1cm4gZVtpXS5jYWxsKG8uZXhwb3J0cyxvLG8uZXhwb3J0cyxyKSxvLmV4cG9ydHN9dmFyIGk9e307cmV0dXJuKCgpPT57dmFyIGU9aTtPYmplY3QuZGVmaW5lUHJvcGVydHkoZSwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksZS5UZXJtaW5hbD12b2lkIDA7dmFyIHQ9cigzMjM2KSxuPXIoOTA0Miksbz1yKDc5NzUpLHM9cig3MDkwKSxhPXIoNTc0MSksYz1yKDgyODUpLGw9WyJjb2xzIiwicm93cyJdLHU9ZnVuY3Rpb24oKXtmdW5jdGlvbiBlKGUpe3ZhciByPXRoaXM7dGhpcy5fY29yZT1uZXcgdC5UZXJtaW5hbChlKSx0aGlzLl9hZGRvbk1hbmFnZXI9bmV3IGEuQWRkb25NYW5hZ2VyLHRoaXMuX3B1YmxpY09wdGlvbnM9e307dmFyIGk9ZnVuY3Rpb24oZSl7T2JqZWN0LmRlZmluZVByb3BlcnR5KG4uX3B1YmxpY09wdGlvbnMsZSx7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHIuX2NvcmUub3B0aW9uc1tlXX0sc2V0OmZ1bmN0aW9uKHQpe3IuX2NoZWNrUmVhZG9ubHlPcHRpb25zKGUpLHIuX2NvcmUub3B0aW9uc1tlXT10fX0pfSxuPXRoaXM7Zm9yKHZhciBvIGluIHRoaXMuX2NvcmUub3B0aW9ucylpKG8pfXJldHVybiBlLnByb3RvdHlwZS5fY2hlY2tSZWFkb25seU9wdGlvbnM9ZnVuY3Rpb24oZSl7aWYobC5pbmNsdWRlcyhlKSl0aHJvdyBuZXcgRXJyb3IoJ09wdGlvbiAiJytlKyciIGNhbiBvbmx5IGJlIHNldCBpbiB0aGUgY29uc3RydWN0b3InKX0sZS5wcm90b3R5cGUuX2NoZWNrUHJvcG9zZWRBcGk9ZnVuY3Rpb24oKXtpZighdGhpcy5fY29yZS5vcHRpb25zU2VydmljZS5vcHRpb25zLmFsbG93UHJvcG9zZWRBcGkpdGhyb3cgbmV3IEVycm9yKCJZb3UgbXVzdCBzZXQgdGhlIGFsbG93UHJvcG9zZWRBcGkgb3B0aW9uIHRvIHRydWUgdG8gdXNlIHByb3Bvc2VkIEFQSSIpfSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uQmVsbCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uQmVsbH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uQmluYXJ5Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUub25CaW5hcnl9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvbkN1cnNvck1vdmUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS5vbkN1cnNvck1vdmV9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvbkRhdGEiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS5vbkRhdGF9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvbktleSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uS2V5fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25MaW5lRmVlZCIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uTGluZUZlZWR9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvblJlbmRlciIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uUmVuZGVyfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25SZXNpemUiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS5vblJlc2l6ZX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsIm9uU2Nyb2xsIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUub25TY3JvbGx9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJvblNlbGVjdGlvbkNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uU2VsZWN0aW9uQ2hhbmdlfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib25UaXRsZUNoYW5nZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLm9uVGl0bGVDaGFuZ2V9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJlbGVtZW50Iix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUuZWxlbWVudH0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsInBhcnNlciIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jaGVja1Byb3Bvc2VkQXBpKCksdGhpcy5fcGFyc2VyfHwodGhpcy5fcGFyc2VyPW5ldyBvLlBhcnNlckFwaSh0aGlzLl9jb3JlKSksdGhpcy5fcGFyc2VyfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwidW5pY29kZSIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9jaGVja1Byb3Bvc2VkQXBpKCksbmV3IHMuVW5pY29kZUFwaSh0aGlzLl9jb3JlKX0sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsInRleHRhcmVhIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUudGV4dGFyZWF9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJyb3dzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NvcmUucm93c30sZW51bWVyYWJsZTohMSxjb25maWd1cmFibGU6ITB9KSxPYmplY3QuZGVmaW5lUHJvcGVydHkoZS5wcm90b3R5cGUsImNvbHMiLHtnZXQ6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5fY29yZS5jb2xzfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwiYnVmZmVyIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NoZWNrUHJvcG9zZWRBcGkoKSx0aGlzLl9idWZmZXJ8fCh0aGlzLl9idWZmZXI9bmV3IGMuQnVmZmVyTmFtZXNwYWNlQXBpKHRoaXMuX2NvcmUpKSx0aGlzLl9idWZmZXJ9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJtYXJrZXJzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuX2NoZWNrUHJvcG9zZWRBcGkoKSx0aGlzLl9jb3JlLm1hcmtlcnN9LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksT2JqZWN0LmRlZmluZVByb3BlcnR5KGUucHJvdG90eXBlLCJtb2RlcyIse2dldDpmdW5jdGlvbigpe3ZhciBlPXRoaXMuX2NvcmUuY29yZVNlcnZpY2UuZGVjUHJpdmF0ZU1vZGVzLHQ9Im5vbmUiO3N3aXRjaCh0aGlzLl9jb3JlLmNvcmVNb3VzZVNlcnZpY2UuYWN0aXZlUHJvdG9jb2wpe2Nhc2UiWDEwIjp0PSJ4MTAiO2JyZWFrO2Nhc2UiVlQyMDAiOnQ9InZ0MjAwIjticmVhaztjYXNlIkRSQUciOnQ9ImRyYWciO2JyZWFrO2Nhc2UiQU5ZIjp0PSJhbnkifXJldHVybnthcHBsaWNhdGlvbkN1cnNvcktleXNNb2RlOmUuYXBwbGljYXRpb25DdXJzb3JLZXlzLGFwcGxpY2F0aW9uS2V5cGFkTW9kZTplLmFwcGxpY2F0aW9uS2V5cGFkLGJyYWNrZXRlZFBhc3RlTW9kZTplLmJyYWNrZXRlZFBhc3RlTW9kZSxpbnNlcnRNb2RlOnRoaXMuX2NvcmUuY29yZVNlcnZpY2UubW9kZXMuaW5zZXJ0TW9kZSxtb3VzZVRyYWNraW5nTW9kZTp0LG9yaWdpbk1vZGU6ZS5vcmlnaW4scmV2ZXJzZVdyYXBhcm91bmRNb2RlOmUucmV2ZXJzZVdyYXBhcm91bmQsc2VuZEZvY3VzTW9kZTplLnNlbmRGb2N1cyx3cmFwYXJvdW5kTW9kZTplLndyYXBhcm91bmR9fSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLnByb3RvdHlwZSwib3B0aW9ucyIse2dldDpmdW5jdGlvbigpe3JldHVybiB0aGlzLl9wdWJsaWNPcHRpb25zfSxzZXQ6ZnVuY3Rpb24oZSl7Zm9yKHZhciB0IGluIGUpdGhpcy5fcHVibGljT3B0aW9uc1t0XT1lW3RdfSxlbnVtZXJhYmxlOiExLGNvbmZpZ3VyYWJsZTohMH0pLGUucHJvdG90eXBlLmJsdXI9ZnVuY3Rpb24oKXt0aGlzLl9jb3JlLmJsdXIoKX0sZS5wcm90b3R5cGUuZm9jdXM9ZnVuY3Rpb24oKXt0aGlzLl9jb3JlLmZvY3VzKCl9LGUucHJvdG90eXBlLnJlc2l6ZT1mdW5jdGlvbihlLHQpe3RoaXMuX3ZlcmlmeUludGVnZXJzKGUsdCksdGhpcy5fY29yZS5yZXNpemUoZSx0KX0sZS5wcm90b3R5cGUub3Blbj1mdW5jdGlvbihlKXt0aGlzLl9jb3JlLm9wZW4oZSl9LGUucHJvdG90eXBlLmF0dGFjaEN1c3RvbUtleUV2ZW50SGFuZGxlcj1mdW5jdGlvbihlKXt0aGlzLl9jb3JlLmF0dGFjaEN1c3RvbUtleUV2ZW50SGFuZGxlcihlKX0sZS5wcm90b3R5cGUucmVnaXN0ZXJMaW5rTWF0Y2hlcj1mdW5jdGlvbihlLHQscil7cmV0dXJuIHRoaXMuX2NoZWNrUHJvcG9zZWRBcGkoKSx0aGlzLl9jb3JlLnJlZ2lzdGVyTGlua01hdGNoZXIoZSx0LHIpfSxlLnByb3RvdHlwZS5kZXJlZ2lzdGVyTGlua01hdGNoZXI9ZnVuY3Rpb24oZSl7dGhpcy5fY2hlY2tQcm9wb3NlZEFwaSgpLHRoaXMuX2NvcmUuZGVyZWdpc3RlckxpbmtNYXRjaGVyKGUpfSxlLnByb3RvdHlwZS5yZWdpc3RlckxpbmtQcm92aWRlcj1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fY2hlY2tQcm9wb3NlZEFwaSgpLHRoaXMuX2NvcmUucmVnaXN0ZXJMaW5rUHJvdmlkZXIoZSl9LGUucHJvdG90eXBlLnJlZ2lzdGVyQ2hhcmFjdGVySm9pbmVyPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9jaGVja1Byb3Bvc2VkQXBpKCksdGhpcy5fY29yZS5yZWdpc3RlckNoYXJhY3RlckpvaW5lcihlKX0sZS5wcm90b3R5cGUuZGVyZWdpc3RlckNoYXJhY3RlckpvaW5lcj1mdW5jdGlvbihlKXt0aGlzLl9jaGVja1Byb3Bvc2VkQXBpKCksdGhpcy5fY29yZS5kZXJlZ2lzdGVyQ2hhcmFjdGVySm9pbmVyKGUpfSxlLnByb3RvdHlwZS5yZWdpc3Rlck1hcmtlcj1mdW5jdGlvbihlKXtyZXR1cm4gdGhpcy5fY2hlY2tQcm9wb3NlZEFwaSgpLHRoaXMuX3ZlcmlmeUludGVnZXJzKGUpLHRoaXMuX2NvcmUuYWRkTWFya2VyKGUpfSxlLnByb3RvdHlwZS5hZGRNYXJrZXI9ZnVuY3Rpb24oZSl7cmV0dXJuIHRoaXMucmVnaXN0ZXJNYXJrZXIoZSl9LGUucHJvdG90eXBlLmhhc1NlbGVjdGlvbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLmhhc1NlbGVjdGlvbigpfSxlLnByb3RvdHlwZS5zZWxlY3Q9ZnVuY3Rpb24oZSx0LHIpe3RoaXMuX3ZlcmlmeUludGVnZXJzKGUsdCxyKSx0aGlzLl9jb3JlLnNlbGVjdChlLHQscil9LGUucHJvdG90eXBlLmdldFNlbGVjdGlvbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLmdldFNlbGVjdGlvbigpfSxlLnByb3RvdHlwZS5nZXRTZWxlY3Rpb25Qb3NpdGlvbj1mdW5jdGlvbigpe3JldHVybiB0aGlzLl9jb3JlLmdldFNlbGVjdGlvblBvc2l0aW9uKCl9LGUucHJvdG90eXBlLmNsZWFyU2VsZWN0aW9uPWZ1bmN0aW9uKCl7dGhpcy5fY29yZS5jbGVhclNlbGVjdGlvbigpfSxlLnByb3RvdHlwZS5zZWxlY3RBbGw9ZnVuY3Rpb24oKXt0aGlzLl9jb3JlLnNlbGVjdEFsbCgpfSxlLnByb3RvdHlwZS5zZWxlY3RMaW5lcz1mdW5jdGlvbihlLHQpe3RoaXMuX3ZlcmlmeUludGVnZXJzKGUsdCksdGhpcy5fY29yZS5zZWxlY3RMaW5lcyhlLHQpfSxlLnByb3RvdHlwZS5kaXNwb3NlPWZ1bmN0aW9uKCl7dGhpcy5fYWRkb25NYW5hZ2VyLmRpc3Bvc2UoKSx0aGlzLl9jb3JlLmRpc3Bvc2UoKX0sZS5wcm90b3R5cGUuc2Nyb2xsTGluZXM9ZnVuY3Rpb24oZSl7dGhpcy5fdmVyaWZ5SW50ZWdlcnMoZSksdGhpcy5fY29yZS5zY3JvbGxMaW5lcyhlKX0sZS5wcm90b3R5cGUuc2Nyb2xsUGFnZXM9ZnVuY3Rpb24oZSl7dGhpcy5fdmVyaWZ5SW50ZWdlcnMoZSksdGhpcy5fY29yZS5zY3JvbGxQYWdlcyhlKX0sZS5wcm90b3R5cGUuc2Nyb2xsVG9Ub3A9ZnVuY3Rpb24oKXt0aGlzLl9jb3JlLnNjcm9sbFRvVG9wKCl9LGUucHJvdG90eXBlLnNjcm9sbFRvQm90dG9tPWZ1bmN0aW9uKCl7dGhpcy5fY29yZS5zY3JvbGxUb0JvdHRvbSgpfSxlLnByb3RvdHlwZS5zY3JvbGxUb0xpbmU9ZnVuY3Rpb24oZSl7dGhpcy5fdmVyaWZ5SW50ZWdlcnMoZSksdGhpcy5fY29yZS5zY3JvbGxUb0xpbmUoZSl9LGUucHJvdG90eXBlLmNsZWFyPWZ1bmN0aW9uKCl7dGhpcy5fY29yZS5jbGVhcigpfSxlLnByb3RvdHlwZS53cml0ZT1mdW5jdGlvbihlLHQpe3RoaXMuX2NvcmUud3JpdGUoZSx0KX0sZS5wcm90b3R5cGUud3JpdGVVdGY4PWZ1bmN0aW9uKGUsdCl7dGhpcy5fY29yZS53cml0ZShlLHQpfSxlLnByb3RvdHlwZS53cml0ZWxuPWZ1bmN0aW9uKGUsdCl7dGhpcy5fY29yZS53cml0ZShlKSx0aGlzLl9jb3JlLndyaXRlKCJcclxuIix0KX0sZS5wcm90b3R5cGUucGFzdGU9ZnVuY3Rpb24oZSl7dGhpcy5fY29yZS5wYXN0ZShlKX0sZS5wcm90b3R5cGUuZ2V0T3B0aW9uPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9jb3JlLm9wdGlvbnNTZXJ2aWNlLmdldE9wdGlvbihlKX0sZS5wcm90b3R5cGUuc2V0T3B0aW9uPWZ1bmN0aW9uKGUsdCl7dGhpcy5fY2hlY2tSZWFkb25seU9wdGlvbnMoZSksdGhpcy5fY29yZS5vcHRpb25zU2VydmljZS5zZXRPcHRpb24oZSx0KX0sZS5wcm90b3R5cGUucmVmcmVzaD1mdW5jdGlvbihlLHQpe3RoaXMuX3ZlcmlmeUludGVnZXJzKGUsdCksdGhpcy5fY29yZS5yZWZyZXNoKGUsdCl9LGUucHJvdG90eXBlLnJlc2V0PWZ1bmN0aW9uKCl7dGhpcy5fY29yZS5yZXNldCgpfSxlLnByb3RvdHlwZS5jbGVhclRleHR1cmVBdGxhcz1mdW5jdGlvbigpe3RoaXMuX2NvcmUuY2xlYXJUZXh0dXJlQXRsYXMoKX0sZS5wcm90b3R5cGUubG9hZEFkZG9uPWZ1bmN0aW9uKGUpe3JldHVybiB0aGlzLl9hZGRvbk1hbmFnZXIubG9hZEFkZG9uKHRoaXMsZSl9LE9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLCJzdHJpbmdzIix7Z2V0OmZ1bmN0aW9uKCl7cmV0dXJuIG59LGVudW1lcmFibGU6ITEsY29uZmlndXJhYmxlOiEwfSksZS5wcm90b3R5cGUuX3ZlcmlmeUludGVnZXJzPWZ1bmN0aW9uKCl7Zm9yKHZhciBlPVtdLHQ9MDt0PGFyZ3VtZW50cy5sZW5ndGg7dCsrKWVbdF09YXJndW1lbnRzW3RdO2Zvcih2YXIgcj0wLGk9ZTtyPGkubGVuZ3RoO3IrKyl7dmFyIG49aVtyXTtpZihuPT09MS8wfHxpc05hTihuKXx8biUxIT0wKXRocm93IG5ldyBFcnJvcigiVGhpcyBBUEkgb25seSBhY2NlcHRzIGludGVnZXJzIil9fSxlfSgpO2UuVGVybWluYWw9dX0pKCksaX0pKCl9fSx0PXt9O2Z1bmN0aW9uIHIoaSl7dmFyIG49dFtpXTtpZih2b2lkIDAhPT1uKXJldHVybiBuLmV4cG9ydHM7dmFyIG89dFtpXT17aWQ6aSxsb2FkZWQ6ITEsZXhwb3J0czp7fX07cmV0dXJuIGVbaV0uY2FsbChvLmV4cG9ydHMsbyxvLmV4cG9ydHMsciksby5sb2FkZWQ9ITAsby5leHBvcnRzfXIubj1lPT57dmFyIHQ9ZSYmZS5fX2VzTW9kdWxlPygpPT5lLmRlZmF1bHQ6KCk9PmU7cmV0dXJuIHIuZCh0LHthOnR9KSx0fSxyLmQ9KGUsdCk9Pntmb3IodmFyIGkgaW4gdClyLm8odCxpKSYmIXIubyhlLGkpJiZPYmplY3QuZGVmaW5lUHJvcGVydHkoZSxpLHtlbnVtZXJhYmxlOiEwLGdldDp0W2ldfSl9LHIuZz1mdW5jdGlvbigpe2lmKCJvYmplY3QiPT10eXBlb2YgZ2xvYmFsVGhpcylyZXR1cm4gZ2xvYmFsVGhpczt0cnl7cmV0dXJuIHRoaXN8fG5ldyBGdW5jdGlvbigicmV0dXJuIHRoaXMiKSgpfWNhdGNoKGUpe2lmKCJvYmplY3QiPT10eXBlb2Ygd2luZG93KXJldHVybiB3aW5kb3d9fSgpLHIubz0oZSx0KT0+T2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKGUsdCksci5ubWQ9ZT0+KGUucGF0aHM9W10sZS5jaGlsZHJlbnx8KGUuY2hpbGRyZW49W10pLGUpLCgoKT0+eyJ1c2Ugc3RyaWN0Ijt2YXIgZT1yKDM3OSksdD1yLm4oZSksaT1yKDc5NSksbj1yLm4oaSksbz1yKDU2OSkscz1yLm4obyksYT1yKDU2NSksYz1yLm4oYSksbD1yKDIxNiksdT1yLm4obCksaD1yKDU4OSksZj1yLm4oaCksXz1yKDEwMiksZD17fTtkLnN0eWxlVGFnVHJhbnNmb3JtPWYoKSxkLnNldEF0dHJpYnV0ZXM9YygpLGQuaW5zZXJ0PXMoKS5iaW5kKG51bGwsImhlYWQiKSxkLmRvbUFQST1uKCksZC5pbnNlcnRTdHlsZUVsZW1lbnQ9dSgpLHQoKShfLlosZCksXy5aJiZfLloubG9jYWxzJiZfLloubG9jYWxzO3ZhciBwPXIoMzIwKSx2PXIoNjE3KSxnPXIoNDg2KSx5PXIubihnKSxtPWZ1bmN0aW9uKGUsdCxyLGkpe3JldHVybiBuZXcocnx8KHI9UHJvbWlzZSkpKChmdW5jdGlvbihuLG8pe2Z1bmN0aW9uIHMoZSl7dHJ5e2MoaS5uZXh0KGUpKX1jYXRjaChlKXtvKGUpfX1mdW5jdGlvbiBhKGUpe3RyeXtjKGkudGhyb3coZSkpfWNhdGNoKGUpe28oZSl9fWZ1bmN0aW9uIGMoZSl7dmFyIHQ7ZS5kb25lP24oZS52YWx1ZSk6KHQ9ZS52YWx1ZSx0IGluc3RhbmNlb2Ygcj90Om5ldyByKChmdW5jdGlvbihlKXtlKHQpfSkpKS50aGVuKHMsYSl9YygoaT1pLmFwcGx5KGUsdHx8W10pKS5uZXh0KCkpfSkpfSxiPWZ1bmN0aW9uKGUsdCl7dmFyIHIsaSxuLG8scz17bGFiZWw6MCxzZW50OmZ1bmN0aW9uKCl7aWYoMSZuWzBdKXRocm93IG5bMV07cmV0dXJuIG5bMV19LHRyeXM6W10sb3BzOltdfTtyZXR1cm4gbz17bmV4dDphKDApLHRocm93OmEoMSkscmV0dXJuOmEoMil9LCJmdW5jdGlvbiI9PXR5cGVvZiBTeW1ib2wmJihvW1N5bWJvbC5pdGVyYXRvcl09ZnVuY3Rpb24oKXtyZXR1cm4gdGhpc30pLG87ZnVuY3Rpb24gYShvKXtyZXR1cm4gZnVuY3Rpb24oYSl7cmV0dXJuIGZ1bmN0aW9uKG8pe2lmKHIpdGhyb3cgbmV3IFR5cGVFcnJvcigiR2VuZXJhdG9yIGlzIGFscmVhZHkgZXhlY3V0aW5nLiIpO2Zvcig7czspdHJ5e2lmKHI9MSxpJiYobj0yJm9bMF0/aS5yZXR1cm46b1swXT9pLnRocm93fHwoKG49aS5yZXR1cm4pJiZuLmNhbGwoaSksMCk6aS5uZXh0KSYmIShuPW4uY2FsbChpLG9bMV0pKS5kb25lKXJldHVybiBuO3N3aXRjaChpPTAsbiYmKG89WzImb1swXSxuLnZhbHVlXSksb1swXSl7Y2FzZSAwOmNhc2UgMTpuPW87YnJlYWs7Y2FzZSA0OnJldHVybiBzLmxhYmVsKysse3ZhbHVlOm9bMV0sZG9uZTohMX07Y2FzZSA1OnMubGFiZWwrKyxpPW9bMV0sbz1bMF07Y29udGludWU7Y2FzZSA3Om89cy5vcHMucG9wKCkscy50cnlzLnBvcCgpO2NvbnRpbnVlO2RlZmF1bHQ6aWYoISgobj0obj1zLnRyeXMpLmxlbmd0aD4wJiZuW24ubGVuZ3RoLTFdKXx8NiE9PW9bMF0mJjIhPT1vWzBdKSl7cz0wO2NvbnRpbnVlfWlmKDM9PT1vWzBdJiYoIW58fG9bMV0+blswXSYmb1sxXTxuWzNdKSl7cy5sYWJlbD1vWzFdO2JyZWFrfWlmKDY9PT1vWzBdJiZzLmxhYmVsPG5bMV0pe3MubGFiZWw9blsxXSxuPW87YnJlYWt9aWYobiYmcy5sYWJlbDxuWzJdKXtzLmxhYmVsPW5bMl0scy5vcHMucHVzaChvKTticmVha31uWzJdJiZzLm9wcy5wb3AoKSxzLnRyeXMucG9wKCk7Y29udGludWV9bz10LmNhbGwoZSxzKX1jYXRjaChlKXtvPVs2LGVdLGk9MH1maW5hbGx5e3I9bj0wfWlmKDUmb1swXSl0aHJvdyBvWzFdO3JldHVybnt2YWx1ZTpvWzBdP29bMV06dm9pZCAwLGRvbmU6ITB9fShbbyxhXSl9fX07d2luZG93Lm9ubG9hZD1mdW5jdGlvbigpe3ZhciBlPW5ldyBwLlRlcm1pbmFsLHQ9bmV3IHYuRml0QWRkb247d2luZG93LnRlcm09ZSx3aW5kb3cuZml0QWRkb249dCxlLmxvYWRBZGRvbih0KSxlLm9wZW4oZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInRlcm1pbmFsIikpO3ZhciByPWZ1bmN0aW9uKCl7ZS5lbGVtZW50LnBhcmVudEVsZW1lbnQuc3R5bGUuaGVpZ2h0PXdpbmRvdy5pbm5lckhlaWdodC0xNisicHgiLHQuZml0KCksZmV0Y2goIi9yZXNpemU/cm93cz0iK2Uucm93cysiJmNvbHM9IitlLmNvbHMpfTtyKCksd2luZG93Lm9ucmVzaXplPXI7dmFyIGk9W107ZS5vbkRhdGEoKGZ1bmN0aW9uKGUpe2kucHVzaChlKX0pKSxtKHRoaXMsdm9pZCAwLHZvaWQgMCwoZnVuY3Rpb24oKXt2YXIgZSx0LHI7cmV0dXJuIGIodGhpcywoZnVuY3Rpb24obil7c3dpdGNoKG4ubGFiZWwpe2Nhc2UgMDplPWZ1bmN0aW9uKGUpe3JldHVybiBuZXcgUHJvbWlzZSgoZnVuY3Rpb24odCl7cmV0dXJuIHNldFRpbWVvdXQodCxlKX0pKX0sbi5sYWJlbD0xO2Nhc2UgMTpuLnRyeXMucHVzaChbMSwsNyw4XSksbi5sYWJlbD0yO2Nhc2UgMjpyZXR1cm5bNCxlKDEwMCldO2Nhc2UgMzpyZXR1cm4gbi5zZW50KCkseSgpLmlzRW1wdHkoaSk/WzMsNV06KHQ9aS5qb2luKCIiKSxyPXdpbmRvdy5idG9hKHQpLGkubGVuZ3RoPTAsWzQsZmV0Y2goIi9pbi8iK3IpXSk7Y2FzZSA0Om4uc2VudCgpLG4ubGFiZWw9NTtjYXNlIDU6cmV0dXJuWzMsMl07Y2FzZSA2OnJldHVyblszLDhdO2Nhc2UgNzpyZXR1cm4gY29uc29sZS5sb2coImlucHV0IGRpc2Nvbm5lY3QhIiksWzddO2Nhc2UgODpyZXR1cm5bMl19fSkpfSkpLGZ1bmN0aW9uKCl7bSh0aGlzLHZvaWQgMCx2b2lkIDAsKGZ1bmN0aW9uKCl7dmFyIHQscixpO3JldHVybiBiKHRoaXMsKGZ1bmN0aW9uKG4pe3N3aXRjaChuLmxhYmVsKXtjYXNlIDA6bi50cnlzLnB1c2goWzAsLDUsNl0pLG4ubGFiZWw9MTtjYXNlIDE6cmV0dXJuWzQsZmV0Y2goIi9vdXQiKV07Y2FzZSAyOnJldHVybiB0PW4uc2VudCgpLGk9VWludDhBcnJheS5iaW5kLFs0LHQuYXJyYXlCdWZmZXIoKV07Y2FzZSAzOnJldHVybiByPW5ldyhpLmFwcGx5KFVpbnQ4QXJyYXksW3ZvaWQgMCxuLnNlbnQoKV0pKSx0JiZlLndyaXRlKHIpLFszLDFdO2Nhc2UgNDpyZXR1cm5bMyw2XTtjYXNlIDU6cmV0dXJuIGNvbnNvbGUubG9nKCJpbnB1dCBkaXNjb25uZWN0ISIpLFs3XTtjYXNlIDY6cmV0dXJuWzJdfX0pKX0pKX0oKX19KSgpfSkoKTs=", - "ok": true, "headers": [ [ "content-length", @@ -2634,12 +2713,12 @@ "text/javascript" ] ], + "ok": true, "status": 200, "status_text": "" }, "https://localhost:10000/out": { "data": "W3N1cGVyZ2F0ZXdheV0gUE9TVCAvbWVzc2FnZSAtPiBTU0UgdHJhbnNwb3J0DQpbc3VwZXJnYXRld2F5XSBTU0UgLT4gQ2hpbGQ6IHsianNvbnJwYyI6IjIuMCIsImlkIjowLCJtZXRob2QiOiJpbml0aWFsaXplIiwicGFyYW1zIjp7InByb3RvY29sVmVyc2lvbiI6IjIwMjQtMTEtMDUiLCJjYXBhYmlsaXRpZXMiOnsicm9vdHMiOnsibGlzdENoYW5nZWQiOnRydWV9fSwiY2xpZW50SW5mbyI6eyJuYW1lIjoibWNwIiwidmVyc2lvbiI6IjAuMS4wIn19fQ0KW3N1cGVyZ2F0ZXdheV0gQ2hpbGQgLT4gU1NFOiB7DQogIHJlc3VsdDogew0KICAgIHByb3RvY29sVmVyc2lvbjogG1szMm0nMjAyNC0xMS0wNScbWzM5bSwNCiAgICBjYXBhYmlsaXRpZXM6IHsgdG9vbHM6IHt9IH0sDQogICAgc2VydmVySW5mbzogeyBuYW1lOiAbWzMybSdzZWN1cmUtZmlsZXN5c3RlbS1zZXJ2ZXInG1szOW0sIHZlcnNpb246IBtbMzJtJzAuMi4wJxtbMzltIH0NCiAgfSwNCiAganNvbnJwYzogG1szMm0nMi4wJxtbMzltLA0KICBpZDogG1szM20wG1szOW0NCn0NCltzdXBlcmdhdGV3YXldIFBPU1QgL21lc3NhZ2UgLT4gU1NFIHRyYW5zcG9ydA0KW3N1cGVyZ2F0ZXdheV0gU1NFIC0+IENoaWxkOiB7Impzb25ycGMiOiIyLjAiLCJtZXRob2QiOiJub3RpZmljYXRpb25zL2luaXRpYWxpemVkIn0NCltzdXBlcmdhdGV3YXldIFBPU1QgL21lc3NhZ2UgLT4gU1NFIHRyYW5zcG9ydA0KW3N1cGVyZ2F0ZXdheV0gU1NFIC0+IENoaWxkOiB7Impzb25ycGMiOiIyLjAiLCJpZCI6MSwibWV0aG9kIjoidG9vbHMvY2FsbCIsInBhcmFtcyI6eyJuYW1lIjoibGlzdF9kaXJlY3RvcnkiLCJhcmd1bWVudHMiOnsic2Vzc2lvbl9pZCI6IjI1ZmU0OWQwLTg4YzAtNGQ3OC05MDFhLWI3YmQyMTBhNGQ1MiIsInBhdGgiOiIvY29udGVudCJ9fX0NCltzdXBlcmdhdGV3YXldIENoaWxkIC0+IFNTRTogeyByZXN1bHQ6IHsgY29udGVudDogWyAbWzM2bVtPYmplY3RdG1szOW0gXSB9LCBqc29ucnBjOiAbWzMybScyLjAnG1szOW0sIGlkOiAbWzMzbTEbWzM5bSB9DQpbc3VwZXJnYXRld2F5XSBTU0UgY29ubmVjdGlvbiBjbG9zZWQuDQo=", - "ok": true, "headers": [ [ "content-length", @@ -2650,12 +2729,12 @@ "text/html; charset=UTF-8" ] ], + "ok": true, "status": 200, "status_text": "" }, "https://localhost:10000/resize?rows=46&cols=196": { "data": "", - "ok": true, "headers": [ [ "content-length", @@ -2666,336 +2745,62 @@ "text/html; charset=UTF-8" ] ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/G1syMDB+bnB4IC15IHN1cGVyZ2F0ZXdheSAtLXBvcnQgODAwMCAtLXN0ZGlvICducHggLXkgQG1vZGVsY29udGV4dHByb3RvY29sL3NlcnZlci1maWxlc3lzdGVtIC9jb250ZW50JxtbMjAxfg==": { - "data": "", "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/DQ==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/Aw==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/DA==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/dA==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/b3U=": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/Yw==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/aCA=": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/Zg==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/bw==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/bw0=": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/dQ==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/Y2g=": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/IA==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/Yg==": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/YXI=": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], - "status": 200, - "status_text": "" - }, - "https://localhost:10000/in/G1tB": { - "data": "", - "ok": true, - "headers": [ - [ - "content-length", - "0" - ], - [ - "content-type", - "text/html; charset=UTF-8" - ] - ], "status": 200, "status_text": "" } - }, - "base_uri": "https://localhost:8080/", - "height": 839 + } }, "id": "giIA2M-ANUIM", "outputId": "612c3487-1fd7-41ab-f65a-690b1325f46d" }, - "id": "giIA2M-ANUIM", - "execution_count": 9, "outputs": [ { - "output_type": "display_data", "data": { "text/plain": [ "Launching Xterm..." ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" }, { - "output_type": "display_data", "data": { + "application/javascript": "\n (async () => {\n const url = new URL(await google.colab.kernel.proxyPort(10000, {'cache': true}));\n const iframe = document.createElement('iframe');\n iframe.src = url;\n iframe.setAttribute('width', '100%');\n iframe.setAttribute('height', '800');\n iframe.setAttribute('frameborder', 0);\n document.body.appendChild(iframe);\n })();\n ", "text/plain": [ "" - ], - "application/javascript": [ - "\n", - " (async () => {\n", - " const url = new URL(await google.colab.kernel.proxyPort(10000, {'cache': true}));\n", - " const iframe = document.createElement('iframe');\n", - " iframe.src = url;\n", - " iframe.setAttribute('width', '100%');\n", - " iframe.setAttribute('height', '800');\n", - " iframe.setAttribute('frameborder', 0);\n", - " document.body.appendChild(iframe);\n", - " })();\n", - " " ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } + ], + "source": [ + "\n", + "%xterm\n", + "# touch /content/foo\n", + "# touch /content/bar\n", + "# npx -y supergateway --port 8000 --stdio 'npx -y @modelcontextprotocol/server-filesystem /content'" ] }, { "cell_type": "markdown", - "source": [ - "Register the toolgroup hosted in the MCP server with llama stack and verify if the stack discovers the tools correctly" - ], + "id": "f4ksBP6MN7cB", "metadata": { "id": "f4ksBP6MN7cB" }, - "id": "f4ksBP6MN7cB" + "source": [ + "Register the toolgroup hosted in the MCP server with llama stack and verify if the stack discovers the tools correctly" + ] }, { "cell_type": "code", + "execution_count": 10, + "id": "DwdKhQb1N295", + "metadata": { + "id": "DwdKhQb1N295" + }, + "outputs": [], "source": [ "from llama_stack_client.types.shared_params.url import URL\n", "client.toolgroups.register(\n", @@ -3003,19 +2808,12 @@ " provider_id=\"model-context-protocol\",\n", " mcp_endpoint=URL(uri=\"http://localhost:8000/sse\"),\n", ")" - ], - "metadata": { - "id": "DwdKhQb1N295" - }, - "id": "DwdKhQb1N295", - "execution_count": 10, - "outputs": [] + ] }, { "cell_type": "code", - "source": [ - "pprint(client.tools.list(toolgroup_id=\"mcp::filesystem\"))" - ], + "execution_count": 11, + "id": "ZZ5_vIkDOyAN", "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -3024,163 +2822,9 @@ "id": "ZZ5_vIkDOyAN", "outputId": "f6fa8639-c2d8-497d-f4ed-716b3bf775d4" }, - "id": "ZZ5_vIkDOyAN", - "execution_count": 11, "outputs": [ { - "output_type": "display_data", "data": { - "text/plain": [ - "\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Read the complete contents of a file from the file system. Handles various text encodings and provides detailed error messages if the file cannot be read. Use this tool when you need to examine the contents of a single file. Only works within allowed directories.'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'read_file'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'read_file'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Read\u001b[0m\u001b[32m the contents of multiple files simultaneously. This is more efficient than reading files one by one when you need to analyze or compare multiple files. Each file's content is returned with its path as a reference. Failed reads for individual files won't stop the entire operation. Only works within allowed directories.\"\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'read_multiple_files'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'paths'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'read_multiple_files'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Create a new file or completely overwrite an existing file with new content. Use with caution as it will overwrite existing files without warning. Handles text content with proper encoding. Only works within allowed directories.'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'write_file'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'content'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'write_file'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Make line-based edits to a text file. Each edit replaces exact line sequences with new content. Returns a git-style diff showing the changes made. Only works within allowed directories.'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'edit_file'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'edits'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Preview changes using git-style diff format'\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'dryRun'\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mparameter_type\u001b[0m=\u001b[32m'boolean'\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'edit_file'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Create a new directory or ensure a directory exists. Can create multiple nested directories in one operation. If the directory already exists, this operation will succeed silently. Perfect for setting up directory structures for projects or ensuring required paths exist. Only works within allowed directories.'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'create_directory'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'create_directory'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Get a detailed listing of all files and directories in a specified path. Results clearly distinguish between files and directories with \u001b[0m\u001b[32m[\u001b[0m\u001b[32mFILE\u001b[0m\u001b[32m]\u001b[0m\u001b[32m and \u001b[0m\u001b[32m[\u001b[0m\u001b[32mDIR\u001b[0m\u001b[32m]\u001b[0m\u001b[32m prefixes. This tool is essential for understanding directory structure and finding specific files within a directory. Only works within allowed directories.'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'list_directory'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'list_directory'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Get\u001b[0m\u001b[32m a recursive tree view of files and directories as a JSON structure. Each entry includes 'name', 'type' \u001b[0m\u001b[32m(\u001b[0m\u001b[32mfile/directory\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, and 'children' for directories. Files have no children array, while directories always have a children array \u001b[0m\u001b[32m(\u001b[0m\u001b[32mwhich may be empty\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. The output is formatted with 2-space indentation for readability. Only works within allowed directories.\"\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'directory_tree'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'directory_tree'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Move or rename files and directories. Can move files between directories and rename them in a single operation. If the destination exists, the operation will fail. Works across different directories and can be used for simple renaming within the same directory. Both source and destination must be within allowed directories.'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'move_file'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'source'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'destination'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'move_file'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Recursively\u001b[0m\u001b[32m search for files and directories matching a pattern. Searches through all subdirectories from the starting path. The search is case-insensitive and matches partial names. Returns full paths to all matching items. Great for finding files when you don't know their exact location. Only searches within allowed directories.\"\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'search_files'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'pattern'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'excludePatterns'\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'search_files'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Retrieve detailed metadata about a file or directory. Returns comprehensive information including size, creation time, last modified time, permissions, and type. This tool is perfect for understanding file characteristics without reading the actual content. Only works within allowed directories.'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'get_file_info'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'get_file_info'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Returns the list of directories that this server is allowed to access. Use this to understand which directories are available before trying to access files.'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'list_allowed_directories'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'list_allowed_directories'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[1m]\u001b[0m\n" - ], "text/html": [ "
[\n",
               "Tool(\n",
@@ -3332,14 +2976,201 @@
               ")\n",
               "]\n",
               "
\n" + ], + "text/plain": [ + "\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Read the complete contents of a file from the file system. Handles various text encodings and provides detailed error messages if the file cannot be read. Use this tool when you need to examine the contents of a single file. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'read_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'read_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Read\u001b[0m\u001b[32m the contents of multiple files simultaneously. This is more efficient than reading files one by one when you need to analyze or compare multiple files. Each file's content is returned with its path as a reference. Failed reads for individual files won't stop the entire operation. Only works within allowed directories.\"\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'read_multiple_files'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'paths'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'read_multiple_files'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Create a new file or completely overwrite an existing file with new content. Use with caution as it will overwrite existing files without warning. Handles text content with proper encoding. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'write_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'content'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'write_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Make line-based edits to a text file. Each edit replaces exact line sequences with new content. Returns a git-style diff showing the changes made. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'edit_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'edits'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Preview changes using git-style diff format'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'dryRun'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mparameter_type\u001b[0m=\u001b[32m'boolean'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'edit_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Create a new directory or ensure a directory exists. Can create multiple nested directories in one operation. If the directory already exists, this operation will succeed silently. Perfect for setting up directory structures for projects or ensuring required paths exist. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'create_directory'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'create_directory'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Get a detailed listing of all files and directories in a specified path. Results clearly distinguish between files and directories with \u001b[0m\u001b[32m[\u001b[0m\u001b[32mFILE\u001b[0m\u001b[32m]\u001b[0m\u001b[32m and \u001b[0m\u001b[32m[\u001b[0m\u001b[32mDIR\u001b[0m\u001b[32m]\u001b[0m\u001b[32m prefixes. This tool is essential for understanding directory structure and finding specific files within a directory. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'list_directory'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'list_directory'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Get\u001b[0m\u001b[32m a recursive tree view of files and directories as a JSON structure. Each entry includes 'name', 'type' \u001b[0m\u001b[32m(\u001b[0m\u001b[32mfile/directory\u001b[0m\u001b[32m)\u001b[0m\u001b[32m, and 'children' for directories. Files have no children array, while directories always have a children array \u001b[0m\u001b[32m(\u001b[0m\u001b[32mwhich may be empty\u001b[0m\u001b[32m)\u001b[0m\u001b[32m. The output is formatted with 2-space indentation for readability. Only works within allowed directories.\"\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'directory_tree'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'directory_tree'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Move or rename files and directories. Can move files between directories and rename them in a single operation. If the destination exists, the operation will fail. Works across different directories and can be used for simple renaming within the same directory. Both source and destination must be within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'move_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'source'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'destination'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'move_file'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m\"Recursively\u001b[0m\u001b[32m search for files and directories matching a pattern. Searches through all subdirectories from the starting path. The search is case-insensitive and matches partial names. Returns full paths to all matching items. Great for finding files when you don't know their exact location. Only searches within allowed directories.\"\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'search_files'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'pattern'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mname\u001b[0m=\u001b[32m'excludePatterns'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mparameter_type\u001b[0m=\u001b[32m'array'\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m,\n", + "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'search_files'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Retrieve detailed metadata about a file or directory. Returns comprehensive information including size, creation time, last modified time, permissions, and type. This tool is perfect for understanding file characteristics without reading the actual content. Only works within allowed directories.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'get_file_info'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1;35mParameter\u001b[0m\u001b[1m(\u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m''\u001b[0m, \u001b[33mname\u001b[0m=\u001b[32m'path'\u001b[0m, \u001b[33mparameter_type\u001b[0m=\u001b[32m'string'\u001b[0m, \u001b[33mrequired\u001b[0m=\u001b[3;92mTrue\u001b[0m, \u001b[33mdefault\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'get_file_info'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m,\n", + "\u001b[2;32m│ \u001b[0m\u001b[1;35mTool\u001b[0m\u001b[1m(\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mdescription\u001b[0m=\u001b[32m'Returns the list of directories that this server is allowed to access. Use this to understand which directories are available before trying to access files.'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'list_allowed_directories'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mparameters\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m]\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'model-context-protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'list_allowed_directories'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtool_host\u001b[0m=\u001b[32m'model_context_protocol'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtoolgroup_id\u001b[0m=\u001b[32m'mcp::filesystem'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'endpoint'\u001b[0m: \u001b[32m'http://localhost:8000/sse'\u001b[0m\u001b[1m}\u001b[0m\n", + "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", + "\u001b[1m]\u001b[0m\n" ] }, - "metadata": {} + "metadata": {}, + "output_type": "display_data" } + ], + "source": [ + "pprint(client.tools.list(toolgroup_id=\"mcp::filesystem\"))" ] }, { "cell_type": "code", + "execution_count": 12, + "id": "vttLbj_YO01f", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vttLbj_YO01f", + "outputId": "04bc486c-3a61-49c6-d0d2-4a211d6de0b5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "User> Hello\n", + "inference> None of the provided functions can be used to respond to a greeting.\n", + "User> list all the files /content\n", + "inference> {\"type\": \"function\", \"name\": \"list_directory\", \"parameters\": {\"path\": \"/content\"}}\n", + "tool_execution> Tool:list_directory Args:{'path': '/content'}\n", + "tool_execution> Tool:list_directory Response:{\"type\":\"text\",\"text\":\"[DIR] .config\\n[FILE] bar\\n[FILE] foo\\n[DIR] sample_data\"}\n", + "inference> {\"type\": \"function\", \"name\": \"list_directory\", \"parameters\": {\"path\": \"/content\"}}\n", + "tool_execution> Tool:list_directory Args:{'path': '/content'}\n", + "tool_execution> Tool:list_directory Response:{\"type\":\"text\",\"text\":\"[DIR] .config\\n[FILE] bar\\n[FILE] foo\\n[DIR] sample_data\"}\n", + "inference> The list of files in the /content directory is:\n", + "\n", + "[DIR] .config\n", + "[FILE] bar\n", + "[FILE] foo\n", + "[DIR] sample_data\n" + ] + } + ], "source": [ "from llama_stack_client.lib.agents.agent import Agent\n", "from llama_stack_client.lib.agents.event_logger import EventLogger\n", @@ -3374,38 +3205,6 @@ " )\n", " for log in EventLogger().log(response):\n", " log.print()\n" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "vttLbj_YO01f", - "outputId": "04bc486c-3a61-49c6-d0d2-4a211d6de0b5" - }, - "id": "vttLbj_YO01f", - "execution_count": 12, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "User> Hello\n", - "inference> None of the provided functions can be used to respond to a greeting.\n", - "User> list all the files /content\n", - "inference> {\"type\": \"function\", \"name\": \"list_directory\", \"parameters\": {\"path\": \"/content\"}}\n", - "tool_execution> Tool:list_directory Args:{'path': '/content'}\n", - "tool_execution> Tool:list_directory Response:{\"type\":\"text\",\"text\":\"[DIR] .config\\n[FILE] bar\\n[FILE] foo\\n[DIR] sample_data\"}\n", - "inference> {\"type\": \"function\", \"name\": \"list_directory\", \"parameters\": {\"path\": \"/content\"}}\n", - "tool_execution> Tool:list_directory Args:{'path': '/content'}\n", - "tool_execution> Tool:list_directory Response:{\"type\":\"text\",\"text\":\"[DIR] .config\\n[FILE] bar\\n[FILE] foo\\n[DIR] sample_data\"}\n", - "inference> The list of files in the /content directory is:\n", - "\n", - "[DIR] .config\n", - "[FILE] bar\n", - "[FILE] foo\n", - "[DIR] sample_data\n" - ] - } ] }, { @@ -4056,6 +3855,21 @@ }, "widgets": { "application/vnd.jupyter.widget-state+json": { + "028e291ee53947bbbbc4bfb68c695f5f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "02baf670942347d69c290452de8641e4": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4132,6 +3946,118 @@ "value": 1 } }, + "03bbebd659e64b5d9c29a73570c34854": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "04804c74e1dd43449d5f758cf5d0ba5e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f023175de68445f98a6b01bb40ccdc6d", + "max": 112, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_7389b79a0ff44cd68c7866995d728023", + "value": 112 + } + }, + "07ce54c75e76488ba4019a20b3707061": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "08f9d125018b41c582a0fa1e234315f9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_5472af91737446f4a4a2d92a3f684a45", + "placeholder": "​", + "style": "IPY_MODEL_9fb4368802da4a5a8101ba200d98403a", + "value": " 232k/232k [00:00<00:00, 3.18MB/s]" + } + }, "0ac8e976a32c4f5989392b8088546e00": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4184,6 +4110,21 @@ "width": null } }, + "0b276315be4345be83da1e03905c8495": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "0c2e30d78c234b1b8098d879442d3bac": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4236,6 +4177,207 @@ "width": null } }, + "0c359bc4c94c46acbc9094354a15c33d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0e1b9910a77d4b7fa69cb8926e6547d7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "0e695245b97c4bbc85e349fda3dc07b9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_90432ec1c24b4607a935c94e130cd68d", + "placeholder": "​", + "style": "IPY_MODEL_464147b149824f20afc727751a702fc7", + "value": "README.md: 100%" + } + }, + "0f8bab6b8ed04774b386fe952aae66f1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "101288236cff40b8bb9dbad80dbbc7ee": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0f8bab6b8ed04774b386fe952aae66f1", + "max": 116, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_cfcb6e456c354d99be91f161552f3376", + "value": 116 + } + }, "10bc8be68b5545fd8609824b02499ebf": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4288,6 +4430,36 @@ "width": null } }, + "1231b9e4cab34c33a38bee63543f1e75": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "13eee164dc534424acb9dc9ee37a9465": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "15ae23892b634a9f821a8fcee14e500b": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4326,6 +4498,116 @@ "description_width": "" } }, + "1a277abd5ea44253bc6894bef258b52b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_670905a55b19458da69f83c8bcd511d1", + "placeholder": "​", + "style": "IPY_MODEL_ff54451a48394faaaa9d8cdb690d0718", + "value": "tokenizer.json: 100%" + } + }, + "1e56da93bcf64ff490416d2b66cd3dc0": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "1e6009b9b0684b8fbaa379ea96f111ee": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "1e836106837c4ac7a11b36e700c46b64": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_9e4d0fbb51284a7487c495c7b95a293d", + "placeholder": "​", + "style": "IPY_MODEL_b0f8cf1f79e04b5fb47a810f2c81bd7e", + "value": "config.json: 100%" + } + }, "20a66f9de4ed41c7ac9a8e817898ed9e": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4363,6 +4645,162 @@ "description_width": "" } }, + "22a665deff88477b9372c0350c4c572b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "23b0b2f4f82c4a21846e91d7cea91da5": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "254ce460ce244c99a5afe39d5d51f6b7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "2574b07e4af24715aa89d048cc84e358": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4436,6 +4874,28 @@ "width": null } }, + "26f1430ca7cb4ad5b1b8df1ffdbd32a9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_7cd2d9c9ea7b4d70902ffaff33033078", + "IPY_MODEL_101288236cff40b8bb9dbad80dbbc7ee", + "IPY_MODEL_d5c9977838a249eeab6ef628279b8155" + ], + "layout": "IPY_MODEL_d032d1e7b4b54ba28ac83c1a12b23876" + } + }, "288c9da81b3c4d80a4959753da973f58": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4488,6 +4948,27 @@ "width": null } }, + "29212208db6b432eb4f708cd64258954": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_ef4f63fe9d8f4683a9d20becb6e4e2cb", + "placeholder": "​", + "style": "IPY_MODEL_7508f10c13634e7aa682cfb29c48d9e7", + "value": " 349/349 [00:00<00:00, 19.2kB/s]" + } + }, "29683ef34d5646c687118a2a0cdec6d4": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4561,6 +5042,28 @@ "width": null } }, + "2e713bcc372e48b2a006558db4d1df68": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_1a277abd5ea44253bc6894bef258b52b", + "IPY_MODEL_b3eedd82e7da4ce8b3ded70e49a2afd0", + "IPY_MODEL_6f5c18cb8002471f8b3764effee37324" + ], + "layout": "IPY_MODEL_3bebac362b344e8d9103c5011613f1ea" + } + }, "2eff72cbd9bb4f1ca77213602caa9417": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4583,6 +5086,288 @@ "layout": "IPY_MODEL_10bc8be68b5545fd8609824b02499ebf" } }, + "30798f87a8b848d783fdacd71af5dc04": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "321fce57c158432abeae496ae8a947aa": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "327ff8f5292d47afbfebd3beea187739": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "36b5bc19b2d0407f8ab28ff0da2ce12d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "3703041a499c426bb427ee008c81cde5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_4b22bbacb995425fb32a2368f3685a92", + "IPY_MODEL_49a66eeb9ef74de5ab8904fd90eb7558", + "IPY_MODEL_08f9d125018b41c582a0fa1e234315f9" + ], + "layout": "IPY_MODEL_736c770230644894b85dbc34bd8f1d52" + } + }, + "3bebac362b344e8d9103c5011613f1ea": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "3c18f449359f422f950543bd976fe323": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4598,6 +5383,22 @@ "description_width": "" } }, + "3cb06377e4454f009d6b2aa7aa6ff0a9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, "3ded85d9c34246e88f8ce693eb8025e5": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4650,6 +5451,21 @@ "width": null } }, + "3ebe00201bdb4e119e3b74f684a58345": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "3ec694106303491ea112a257309bc69c": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4790,6 +5606,73 @@ "value": "Batches: 100%" } }, + "4502477db4d948e693012364c2dcb370": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "464147b149824f20afc727751a702fc7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "4709067f3f554b93b3ef35e3f58cbf85": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4828,6 +5711,66 @@ "layout": "IPY_MODEL_e61fdef1dc4b4d809168c0b441b0e6ac" } }, + "47cf4b6b835d43388576a2abf4cc54f8": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "49a66eeb9ef74de5ab8904fd90eb7558": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_1e56da93bcf64ff490416d2b66cd3dc0", + "max": 231508, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_b7e35038ce344110b785753b655130f5", + "value": 231508 + } + }, + "4b22bbacb995425fb32a2368f3685a92": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_b67cbbf32f844a19b219be612d5038c9", + "placeholder": "​", + "style": "IPY_MODEL_774b513d64524ac7823a2cf13efa8d41", + "value": "vocab.txt: 100%" + } + }, "4b83e3caa8ec47169dca04ee9599adeb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4852,6 +5795,293 @@ "value": 1 } }, + "4cf1dc345ace4da59f978f661487f975": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "50dd8994a4cf486ebbec5ffd4322992a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "52fe404ec9c14db2a7279b4c154eef3d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "541b9b4e74614e2cb855bb90f03df538": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "5459633eb6e94ec391d13fcf67425726": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8e81ae00681347cb906b392c3656a64a", + "max": 90868376, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_74bedc38b7da4e8a83b0c892d7aa59b5", + "value": 90868376 + } + }, + "5472af91737446f4a4a2d92a3f684a45": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "55591e8179084fcfa3a61c8bd8d09dcb": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0c359bc4c94c46acbc9094354a15c33d", + "max": 612, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_59d0b59b6c2248508d0601ff13878d33", + "value": 612 + } + }, + "59d0b59b6c2248508d0601ff13878d33": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, "5a620017a5384af1a056de687b2670db": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4868,6 +6098,146 @@ "description_width": "" } }, + "5ce87402a79342af995df41ac3940d55": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_f9b768c703494dd198f2978aff4892e8", + "placeholder": "​", + "style": "IPY_MODEL_1231b9e4cab34c33a38bee63543f1e75", + "value": "modules.json: 100%" + } + }, + "5e535ed2b83e496ab57b1c80b615ab0c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "5f6014ba13fa4a659b9eb1b5f83599a7": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "61bd0d490c0e4c04a331cf9ce6b7d38f": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "631c9a95127244c79875c829a7637df6": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -4920,6 +6290,110 @@ "width": null } }, + "670905a55b19458da69f83c8bcd511d1": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "67e37a088be64a2ba786ca923b1017dd": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "69e5263c812c4542a9e5c31fefaa37fe": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -4935,6 +6409,264 @@ "description_width": "" } }, + "6f5c18cb8002471f8b3764effee37324": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_abce503d70594c2ca9afdc47847c125b", + "placeholder": "​", + "style": "IPY_MODEL_028e291ee53947bbbbc4bfb68c695f5f", + "value": " 466k/466k [00:00<00:00, 3.52MB/s]" + } + }, + "722a7fe16af3422585a20c651345cfa4": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_f5596c1c9c4d42f3bc171961f9582eff", + "IPY_MODEL_85d66e615b5742e78657b1e60c75fc72", + "IPY_MODEL_731c02dc5dd446c3b22765575148e256" + ], + "layout": "IPY_MODEL_254ce460ce244c99a5afe39d5d51f6b7" + } + }, + "72e7c092fb054b7ea0dcd2782b5d8a7d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_327ff8f5292d47afbfebd3beea187739", + "placeholder": "​", + "style": "IPY_MODEL_988cac4341b646079fc73719f3f88ad7", + "value": "tokenizer_config.json: 100%" + } + }, + "731c02dc5dd446c3b22765575148e256": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_4502477db4d948e693012364c2dcb370", + "placeholder": "​", + "style": "IPY_MODEL_52fe404ec9c14db2a7279b4c154eef3d", + "value": " 190/190 [00:00<00:00, 12.8kB/s]" + } + }, + "736c770230644894b85dbc34bd8f1d52": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "7389b79a0ff44cd68c7866995d728023": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "74bedc38b7da4e8a83b0c892d7aa59b5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "7508f10c13634e7aa682cfb29c48d9e7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "75307e3dee604d30aa44713e6e293e64": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_5ce87402a79342af995df41ac3940d55", + "IPY_MODEL_fbbcc19886cc43b38424fbb184162c61", + "IPY_MODEL_29212208db6b432eb4f708cd64258954" + ], + "layout": "IPY_MODEL_50dd8994a4cf486ebbec5ffd4322992a" + } + }, + "754deb3970604d48a522bc9f021ad945": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "7551b282ef3a4387a801637de2d5c76e": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -5002,6 +6734,36 @@ "description_width": "" } }, + "76d37a48a73946bab2821f097cf2605f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "774b513d64524ac7823a2cf13efa8d41": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "7cc356ed20e94401b72a0e138ad0f5df": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5024,6 +6786,42 @@ "layout": "IPY_MODEL_e662ba10fbae49d9b66172125dfc0717" } }, + "7cd2d9c9ea7b4d70902ffaff33033078": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_321fce57c158432abeae496ae8a947aa", + "placeholder": "​", + "style": "IPY_MODEL_3ebe00201bdb4e119e3b74f684a58345", + "value": "config_sentence_transformers.json: 100%" + } + }, + "7d8653fca29f4df3a7487733ff9db60b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "811f115733b14ab4b242a8b11526016c": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5045,6 +6843,240 @@ "value": " 1/1 [00:00<00:00, 13.00it/s]" } }, + "844b06df5749441fab6f61656ce581a9": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_03bbebd659e64b5d9c29a73570c34854", + "max": 53, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_b68e5097d2504d2cbd7e19aa1aac3a04", + "value": 53 + } + }, + "85d66e615b5742e78657b1e60c75fc72": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_dd85d37dd1d14c7ea4592f8e11b2d2c8", + "max": 190, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_3cb06377e4454f009d6b2aa7aa6ff0a9", + "value": 190 + } + }, + "861a00796f55470e85d94733eeee9a5f": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_e2e49c25d6fc4592b317e94cfabc2e5e", + "placeholder": "​", + "style": "IPY_MODEL_76d37a48a73946bab2821f097cf2605f", + "value": "model.safetensors: 100%" + } + }, + "87700a80125348f28c4f249bdf8b0a8d": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_0e1b9910a77d4b7fa69cb8926e6547d7", + "placeholder": "​", + "style": "IPY_MODEL_0b276315be4345be83da1e03905c8495", + "value": " 10.7k/10.7k [00:00<00:00, 862kB/s]" + } + }, + "879e48d9a9e04183903d94ffe98313d2": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "8902c3622da540e496ed5b1524bd01ca": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "891cb726d45c4fef8f2c74a56df5532b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8b1ea80221174fae943d5c9f997dfb57": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_900a4dac08f540dfb35c29f63236a12c", + "max": 350, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_1e6009b9b0684b8fbaa379ea96f111ee", + "value": 350 + } + }, "8d370762fafd4d7887ff68ea8279d083": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -5121,6 +7153,272 @@ "value": 1 } }, + "8e2b70ffe4eb4974bd6393fcc1292267": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8e81ae00681347cb906b392c3656a64a": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "8f30fca71bf24e5ca26e17c2321f893c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "900a4dac08f540dfb35c29f63236a12c": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "90432ec1c24b4607a935c94e130cd68d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "943f8fcb66614353a51f32f8344b6122": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_0e695245b97c4bbc85e349fda3dc07b9", + "IPY_MODEL_bb0d168c41f540b8ae42239d3938483a", + "IPY_MODEL_87700a80125348f28c4f249bdf8b0a8d" + ], + "layout": "IPY_MODEL_8902c3622da540e496ed5b1524bd01ca" + } + }, + "95a506c3007c4525b01ee4e1600d671b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_8e2b70ffe4eb4974bd6393fcc1292267", + "placeholder": "​", + "style": "IPY_MODEL_13eee164dc534424acb9dc9ee37a9465", + "value": " 112/112 [00:00<00:00, 8.09kB/s]" + } + }, "980292182c7144e194604c13ac544a26": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5142,6 +7440,37 @@ "value": "Batches: 100%" } }, + "98786f52ef5345b0b9164b9c1f2b8e18": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "988cac4341b646079fc73719f3f88ad7": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "9bb8bf12010f42b2b17c10c7ccaa7bf8": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5158,6 +7487,58 @@ "description_width": "" } }, + "9dece059f1204e29b106fca9e191ddb3": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "9df914248c214597bed7d7980c7a0afe": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -5210,6 +7591,214 @@ "width": null } }, + "9e4d0fbb51284a7487c495c7b95a293d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "9fb4368802da4a5a8101ba200d98403a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, + "a0d6b0caeb2340fe96c8f5569e3d3ae4": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "a530662719374c95a9bef12e59e28c85": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_bffc0f4b12f141398535990709fd4f2c", + "IPY_MODEL_04804c74e1dd43449d5f758cf5d0ba5e", + "IPY_MODEL_95a506c3007c4525b01ee4e1600d671b" + ], + "layout": "IPY_MODEL_a0d6b0caeb2340fe96c8f5569e3d3ae4" + } + }, + "abce503d70594c2ca9afdc47847c125b": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "abe6cf39b784436993fcbe92221c31a3": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "acd39276db17439798a97abc56460b0f": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5231,6 +7820,21 @@ "value": "Batches: 100%" } }, + "b0f8cf1f79e04b5fb47a810f2c81bd7e": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "DescriptionStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "description_width": "" + } + }, "b28d46c2ecdd46b9b3f2da871afbf1cb": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5252,6 +7856,98 @@ "value": "Batches: 100%" } }, + "b3eedd82e7da4ce8b3ded70e49a2afd0": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_36b5bc19b2d0407f8ab28ff0da2ce12d", + "max": 466247, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_879e48d9a9e04183903d94ffe98313d2", + "value": 466247 + } + }, + "b67cbbf32f844a19b219be612d5038c9": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "b68e5097d2504d2cbd7e19aa1aac3a04": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, "b6a0eb553b024a71b737ff47ca8f7633": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5267,6 +7963,67 @@ "description_width": "" } }, + "b7b7467ece304ffbbd352b9b96a03aad": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_d1e67c28b4664e8098dce8f5e80b8779", + "placeholder": "​", + "style": "IPY_MODEL_abe6cf39b784436993fcbe92221c31a3", + "value": " 90.9M/90.9M [00:00<00:00, 215MB/s]" + } + }, + "b7e35038ce344110b785753b655130f5": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, + "bb0d168c41f540b8ae42239d3938483a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "FloatProgressModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "FloatProgressModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "ProgressView", + "bar_style": "success", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_67e37a088be64a2ba786ca923b1017dd", + "max": 10659, + "min": 0, + "orientation": "horizontal", + "style": "IPY_MODEL_98786f52ef5345b0b9164b9c1f2b8e18", + "value": 10659 + } + }, "bda474c3b8184597a6a9bc6da0672a50": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5291,6 +8048,79 @@ "value": 1 } }, + "bffc0f4b12f141398535990709fd4f2c": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_30798f87a8b848d783fdacd71af5dc04", + "placeholder": "​", + "style": "IPY_MODEL_07ce54c75e76488ba4019a20b3707061", + "value": "special_tokens_map.json: 100%" + } + }, + "c690da8daa1e4f9ea73bcacdd92e8a6d": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "c83c23161674484e81f0db9856c23eb6": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5342,6 +8172,22 @@ "description_width": "" } }, + "cfcb6e456c354d99be91f161552f3376": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "ProgressStyleModel", + "state": { + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "ProgressStyleModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "StyleView", + "bar_color": null, + "description_width": "" + } + }, "cfe6be8fd8254bc084a81b1d06e86ae1": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -5394,6 +8240,184 @@ "width": null } }, + "d021a18ab70b4c7e8aec43932a124c36": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_72e7c092fb054b7ea0dcd2782b5d8a7d", + "IPY_MODEL_8b1ea80221174fae943d5c9f997dfb57", + "IPY_MODEL_f8073d625f80415dbf712cee434f6e3a" + ], + "layout": "IPY_MODEL_5f6014ba13fa4a659b9eb1b5f83599a7" + } + }, + "d032d1e7b4b54ba28ac83c1a12b23876": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d0b161ae25c441e8b3caf7a3d88c1b05": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "d1e67c28b4664e8098dce8f5e80b8779": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "d1f8f4568a444248b69022d58e3f1af0": { "model_module": "@jupyter-widgets/controls", "model_module_version": "1.5.0", @@ -5528,6 +8552,217 @@ "width": null } }, + "d5c9977838a249eeab6ef628279b8155": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_61bd0d490c0e4c04a331cf9ce6b7d38f", + "placeholder": "​", + "style": "IPY_MODEL_7d8653fca29f4df3a7487733ff9db60b", + "value": " 116/116 [00:00<00:00, 5.06kB/s]" + } + }, + "d9de065c7f81443e98ddf066c7b5bd54": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_1e836106837c4ac7a11b36e700c46b64", + "IPY_MODEL_55591e8179084fcfa3a61c8bd8d09dcb", + "IPY_MODEL_de1ef93c41364eda9b4b111231057348" + ], + "layout": "IPY_MODEL_23b0b2f4f82c4a21846e91d7cea91da5" + } + }, + "dd85d37dd1d14c7ea4592f8e11b2d2c8": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, + "de1ef93c41364eda9b4b111231057348": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_891cb726d45c4fef8f2c74a56df5532b", + "placeholder": "​", + "style": "IPY_MODEL_fa39189070334939aea5fa4a7de5ec8b", + "value": " 612/612 [00:00<00:00, 48.3kB/s]" + } + }, + "e11f8c3891284e07bd2572257afd5e1b": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HBoxModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HBoxModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HBoxView", + "box_style": "", + "children": [ + "IPY_MODEL_ee18d96394994d01b49d5b03b3d9a019", + "IPY_MODEL_844b06df5749441fab6f61656ce581a9", + "IPY_MODEL_e1c6b9a20e074f17aeba976b24e80c65" + ], + "layout": "IPY_MODEL_c690da8daa1e4f9ea73bcacdd92e8a6d" + } + }, + "e1c6b9a20e074f17aeba976b24e80c65": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_22a665deff88477b9372c0350c4c572b", + "placeholder": "​", + "style": "IPY_MODEL_5e535ed2b83e496ab57b1c80b615ab0c", + "value": " 53.0/53.0 [00:00<00:00, 4.23kB/s]" + } + }, + "e2e49c25d6fc4592b317e94cfabc2e5e": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "1.2.0", + "model_name": "LayoutModel", + "state": { + "_model_module": "@jupyter-widgets/base", + "_model_module_version": "1.2.0", + "_model_name": "LayoutModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/base", + "_view_module_version": "1.2.0", + "_view_name": "LayoutView", + "align_content": null, + "align_items": null, + "align_self": null, + "border": null, + "bottom": null, + "display": null, + "flex": null, + "flex_flow": null, + "grid_area": null, + "grid_auto_columns": null, + "grid_auto_flow": null, + "grid_auto_rows": null, + "grid_column": null, + "grid_gap": null, + "grid_row": null, + "grid_template_areas": null, + "grid_template_columns": null, + "grid_template_rows": null, + "height": null, + "justify_content": null, + "justify_items": null, + "left": null, + "margin": null, + "max_height": null, + "max_width": null, + "min_height": null, + "min_width": null, + "object_fit": null, + "object_position": null, + "order": null, + "overflow": null, + "overflow_x": null, + "overflow_y": null, + "padding": null, + "right": null, + "top": null, + "visibility": null, + "width": null + } + }, "e61fdef1dc4b4d809168c0b441b0e6ac": { "model_module": "@jupyter-widgets/base", "model_module_version": "1.2.0", @@ -5742,1082 +8977,10 @@ "layout": "IPY_MODEL_3ec694106303491ea112a257309bc69c" } }, - "fe34706489c14253a5015ff6332ec4e0": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_cfe6be8fd8254bc084a81b1d06e86ae1", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_1817f6732a5f44c7adc75a644b1acef2", - "value": 1 - } - }, - "75307e3dee604d30aa44713e6e293e64": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_5ce87402a79342af995df41ac3940d55", - "IPY_MODEL_fbbcc19886cc43b38424fbb184162c61", - "IPY_MODEL_29212208db6b432eb4f708cd64258954" - ], - "layout": "IPY_MODEL_50dd8994a4cf486ebbec5ffd4322992a" - } - }, - "5ce87402a79342af995df41ac3940d55": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_f9b768c703494dd198f2978aff4892e8", - "placeholder": "​", - "style": "IPY_MODEL_1231b9e4cab34c33a38bee63543f1e75", - "value": "modules.json: 100%" - } - }, - "fbbcc19886cc43b38424fbb184162c61": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_754deb3970604d48a522bc9f021ad945", - "max": 349, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_f6ecca7a1a8340fbbe056235a2714fc3", - "value": 349 - } - }, - "29212208db6b432eb4f708cd64258954": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_ef4f63fe9d8f4683a9d20becb6e4e2cb", - "placeholder": "​", - "style": "IPY_MODEL_7508f10c13634e7aa682cfb29c48d9e7", - "value": " 349/349 [00:00<00:00, 19.2kB/s]" - } - }, - "50dd8994a4cf486ebbec5ffd4322992a": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f9b768c703494dd198f2978aff4892e8": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1231b9e4cab34c33a38bee63543f1e75": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "754deb3970604d48a522bc9f021ad945": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f6ecca7a1a8340fbbe056235a2714fc3": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "ef4f63fe9d8f4683a9d20becb6e4e2cb": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "7508f10c13634e7aa682cfb29c48d9e7": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "26f1430ca7cb4ad5b1b8df1ffdbd32a9": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_7cd2d9c9ea7b4d70902ffaff33033078", - "IPY_MODEL_101288236cff40b8bb9dbad80dbbc7ee", - "IPY_MODEL_d5c9977838a249eeab6ef628279b8155" - ], - "layout": "IPY_MODEL_d032d1e7b4b54ba28ac83c1a12b23876" - } - }, - "7cd2d9c9ea7b4d70902ffaff33033078": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_321fce57c158432abeae496ae8a947aa", - "placeholder": "​", - "style": "IPY_MODEL_3ebe00201bdb4e119e3b74f684a58345", - "value": "config_sentence_transformers.json: 100%" - } - }, - "101288236cff40b8bb9dbad80dbbc7ee": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_0f8bab6b8ed04774b386fe952aae66f1", - "max": 116, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_cfcb6e456c354d99be91f161552f3376", - "value": 116 - } - }, - "d5c9977838a249eeab6ef628279b8155": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_61bd0d490c0e4c04a331cf9ce6b7d38f", - "placeholder": "​", - "style": "IPY_MODEL_7d8653fca29f4df3a7487733ff9db60b", - "value": " 116/116 [00:00<00:00, 5.06kB/s]" - } - }, - "d032d1e7b4b54ba28ac83c1a12b23876": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "321fce57c158432abeae496ae8a947aa": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3ebe00201bdb4e119e3b74f684a58345": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "0f8bab6b8ed04774b386fe952aae66f1": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "cfcb6e456c354d99be91f161552f3376": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "61bd0d490c0e4c04a331cf9ce6b7d38f": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "7d8653fca29f4df3a7487733ff9db60b": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "943f8fcb66614353a51f32f8344b6122": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_0e695245b97c4bbc85e349fda3dc07b9", - "IPY_MODEL_bb0d168c41f540b8ae42239d3938483a", - "IPY_MODEL_87700a80125348f28c4f249bdf8b0a8d" - ], - "layout": "IPY_MODEL_8902c3622da540e496ed5b1524bd01ca" - } - }, - "0e695245b97c4bbc85e349fda3dc07b9": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_90432ec1c24b4607a935c94e130cd68d", - "placeholder": "​", - "style": "IPY_MODEL_464147b149824f20afc727751a702fc7", - "value": "README.md: 100%" - } - }, - "bb0d168c41f540b8ae42239d3938483a": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_67e37a088be64a2ba786ca923b1017dd", - "max": 10659, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_98786f52ef5345b0b9164b9c1f2b8e18", - "value": 10659 - } - }, - "87700a80125348f28c4f249bdf8b0a8d": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_0e1b9910a77d4b7fa69cb8926e6547d7", - "placeholder": "​", - "style": "IPY_MODEL_0b276315be4345be83da1e03905c8495", - "value": " 10.7k/10.7k [00:00<00:00, 862kB/s]" - } - }, - "8902c3622da540e496ed5b1524bd01ca": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "90432ec1c24b4607a935c94e130cd68d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "464147b149824f20afc727751a702fc7": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "67e37a088be64a2ba786ca923b1017dd": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "98786f52ef5345b0b9164b9c1f2b8e18": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "0e1b9910a77d4b7fa69cb8926e6547d7": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "0b276315be4345be83da1e03905c8495": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "e11f8c3891284e07bd2572257afd5e1b": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_ee18d96394994d01b49d5b03b3d9a019", - "IPY_MODEL_844b06df5749441fab6f61656ce581a9", - "IPY_MODEL_e1c6b9a20e074f17aeba976b24e80c65" - ], - "layout": "IPY_MODEL_c690da8daa1e4f9ea73bcacdd92e8a6d" - } - }, "ee18d96394994d01b49d5b03b3d9a019": { "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", "model_module_version": "1.5.0", + "model_name": "HTMLModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -6835,55 +8998,10 @@ "value": "sentence_bert_config.json: 100%" } }, - "844b06df5749441fab6f61656ce581a9": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_03bbebd659e64b5d9c29a73570c34854", - "max": 53, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_b68e5097d2504d2cbd7e19aa1aac3a04", - "value": 53 - } - }, - "e1c6b9a20e074f17aeba976b24e80c65": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_22a665deff88477b9372c0350c4c572b", - "placeholder": "​", - "style": "IPY_MODEL_5e535ed2b83e496ab57b1c80b615ab0c", - "value": " 53.0/53.0 [00:00<00:00, 4.23kB/s]" - } - }, - "c690da8daa1e4f9ea73bcacdd92e8a6d": { + "ef4f63fe9d8f4683a9d20becb6e4e2cb": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -6932,10 +9050,10 @@ "width": null } }, - "d0b161ae25c441e8b3caf7a3d88c1b05": { + "f023175de68445f98a6b01bb40ccdc6d": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -6984,160 +9102,10 @@ "width": null } }, - "47cf4b6b835d43388576a2abf4cc54f8": { + "f0e107dd6d54483aa367da0e337a97cd": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "03bbebd659e64b5d9c29a73570c34854": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b68e5097d2504d2cbd7e19aa1aac3a04": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "22a665deff88477b9372c0350c4c572b": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "5e535ed2b83e496ab57b1c80b615ab0c": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "d9de065c7f81443e98ddf066c7b5bd54": { - "model_module": "@jupyter-widgets/controls", "model_name": "HBoxModel", - "model_module_version": "1.5.0", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -7149,17 +9117,17 @@ "_view_name": "HBoxView", "box_style": "", "children": [ - "IPY_MODEL_1e836106837c4ac7a11b36e700c46b64", - "IPY_MODEL_55591e8179084fcfa3a61c8bd8d09dcb", - "IPY_MODEL_de1ef93c41364eda9b4b111231057348" + "IPY_MODEL_861a00796f55470e85d94733eeee9a5f", + "IPY_MODEL_5459633eb6e94ec391d13fcf67425726", + "IPY_MODEL_b7b7467ece304ffbbd352b9b96a03aad" ], - "layout": "IPY_MODEL_23b0b2f4f82c4a21846e91d7cea91da5" + "layout": "IPY_MODEL_9dece059f1204e29b106fca9e191ddb3" } }, - "1e836106837c4ac7a11b36e700c46b64": { + "f5596c1c9c4d42f3bc171961f9582eff": { "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", "model_module_version": "1.5.0", + "model_name": "HTMLModel", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -7171,232 +9139,16 @@ "_view_name": "HTMLView", "description": "", "description_tooltip": null, - "layout": "IPY_MODEL_9e4d0fbb51284a7487c495c7b95a293d", + "layout": "IPY_MODEL_4cf1dc345ace4da59f978f661487f975", "placeholder": "​", - "style": "IPY_MODEL_b0f8cf1f79e04b5fb47a810f2c81bd7e", - "value": "config.json: 100%" + "style": "IPY_MODEL_8f30fca71bf24e5ca26e17c2321f893c", + "value": "1_Pooling/config.json: 100%" } }, - "55591e8179084fcfa3a61c8bd8d09dcb": { + "f6ecca7a1a8340fbbe056235a2714fc3": { "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_0c359bc4c94c46acbc9094354a15c33d", - "max": 612, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_59d0b59b6c2248508d0601ff13878d33", - "value": 612 - } - }, - "de1ef93c41364eda9b4b111231057348": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_891cb726d45c4fef8f2c74a56df5532b", - "placeholder": "​", - "style": "IPY_MODEL_fa39189070334939aea5fa4a7de5ec8b", - "value": " 612/612 [00:00<00:00, 48.3kB/s]" - } - }, - "23b0b2f4f82c4a21846e91d7cea91da5": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9e4d0fbb51284a7487c495c7b95a293d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b0f8cf1f79e04b5fb47a810f2c81bd7e": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "0c359bc4c94c46acbc9094354a15c33d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "59d0b59b6c2248508d0601ff13878d33": { - "model_module": "@jupyter-widgets/controls", "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -7409,10 +9161,31 @@ "description_width": "" } }, - "891cb726d45c4fef8f2c74a56df5532b": { + "f8073d625f80415dbf712cee434f6e3a": { + "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", + "model_name": "HTMLModel", + "state": { + "_dom_classes": [], + "_model_module": "@jupyter-widgets/controls", + "_model_module_version": "1.5.0", + "_model_name": "HTMLModel", + "_view_count": null, + "_view_module": "@jupyter-widgets/controls", + "_view_module_version": "1.5.0", + "_view_name": "HTMLView", + "description": "", + "description_tooltip": null, + "layout": "IPY_MODEL_541b9b4e74614e2cb855bb90f03df538", + "placeholder": "​", + "style": "IPY_MODEL_ff256b2275f740ed82bca4f43b4d6fd2", + "value": " 350/350 [00:00<00:00, 23.3kB/s]" + } + }, + "f9b768c703494dd198f2978aff4892e8": { "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", "model_module_version": "1.2.0", + "model_name": "LayoutModel", "state": { "_model_module": "@jupyter-widgets/base", "_model_module_version": "1.2.0", @@ -7463,8 +9236,8 @@ }, "fa39189070334939aea5fa4a7de5ec8b": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -7476,53 +9249,10 @@ "description_width": "" } }, - "f0e107dd6d54483aa367da0e337a97cd": { + "fbbcc19886cc43b38424fbb184162c61": { "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_861a00796f55470e85d94733eeee9a5f", - "IPY_MODEL_5459633eb6e94ec391d13fcf67425726", - "IPY_MODEL_b7b7467ece304ffbbd352b9b96a03aad" - ], - "layout": "IPY_MODEL_9dece059f1204e29b106fca9e191ddb3" - } - }, - "861a00796f55470e85d94733eeee9a5f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_e2e49c25d6fc4592b317e94cfabc2e5e", - "placeholder": "​", - "style": "IPY_MODEL_76d37a48a73946bab2821f097cf2605f", - "value": "model.safetensors: 100%" - } - }, - "5459633eb6e94ec391d13fcf67425726": { - "model_module": "@jupyter-widgets/controls", "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -7535,336 +9265,18 @@ "bar_style": "success", "description": "", "description_tooltip": null, - "layout": "IPY_MODEL_8e81ae00681347cb906b392c3656a64a", - "max": 90868376, + "layout": "IPY_MODEL_754deb3970604d48a522bc9f021ad945", + "max": 349, "min": 0, "orientation": "horizontal", - "style": "IPY_MODEL_74bedc38b7da4e8a83b0c892d7aa59b5", - "value": 90868376 + "style": "IPY_MODEL_f6ecca7a1a8340fbbe056235a2714fc3", + "value": 349 } }, - "b7b7467ece304ffbbd352b9b96a03aad": { + "fe34706489c14253a5015ff6332ec4e0": { "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_d1e67c28b4664e8098dce8f5e80b8779", - "placeholder": "​", - "style": "IPY_MODEL_abe6cf39b784436993fcbe92221c31a3", - "value": " 90.9M/90.9M [00:00<00:00, 215MB/s]" - } - }, - "9dece059f1204e29b106fca9e191ddb3": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "e2e49c25d6fc4592b317e94cfabc2e5e": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "76d37a48a73946bab2821f097cf2605f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "8e81ae00681347cb906b392c3656a64a": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "74bedc38b7da4e8a83b0c892d7aa59b5": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "d1e67c28b4664e8098dce8f5e80b8779": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "abe6cf39b784436993fcbe92221c31a3": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "d021a18ab70b4c7e8aec43932a124c36": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_72e7c092fb054b7ea0dcd2782b5d8a7d", - "IPY_MODEL_8b1ea80221174fae943d5c9f997dfb57", - "IPY_MODEL_f8073d625f80415dbf712cee434f6e3a" - ], - "layout": "IPY_MODEL_5f6014ba13fa4a659b9eb1b5f83599a7" - } - }, - "72e7c092fb054b7ea0dcd2782b5d8a7d": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_327ff8f5292d47afbfebd3beea187739", - "placeholder": "​", - "style": "IPY_MODEL_988cac4341b646079fc73719f3f88ad7", - "value": "tokenizer_config.json: 100%" - } - }, - "8b1ea80221174fae943d5c9f997dfb57": { - "model_module": "@jupyter-widgets/controls", "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", "state": { "_dom_classes": [], "_model_module": "@jupyter-widgets/controls", @@ -7877,278 +9289,18 @@ "bar_style": "success", "description": "", "description_tooltip": null, - "layout": "IPY_MODEL_900a4dac08f540dfb35c29f63236a12c", - "max": 350, + "layout": "IPY_MODEL_cfe6be8fd8254bc084a81b1d06e86ae1", + "max": 1, "min": 0, "orientation": "horizontal", - "style": "IPY_MODEL_1e6009b9b0684b8fbaa379ea96f111ee", - "value": 350 - } - }, - "f8073d625f80415dbf712cee434f6e3a": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_541b9b4e74614e2cb855bb90f03df538", - "placeholder": "​", - "style": "IPY_MODEL_ff256b2275f740ed82bca4f43b4d6fd2", - "value": " 350/350 [00:00<00:00, 23.3kB/s]" - } - }, - "5f6014ba13fa4a659b9eb1b5f83599a7": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "327ff8f5292d47afbfebd3beea187739": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "988cac4341b646079fc73719f3f88ad7": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "900a4dac08f540dfb35c29f63236a12c": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "1e6009b9b0684b8fbaa379ea96f111ee": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "541b9b4e74614e2cb855bb90f03df538": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null + "style": "IPY_MODEL_1817f6732a5f44c7adc75a644b1acef2", + "value": 1 } }, "ff256b2275f740ed82bca4f43b4d6fd2": { "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", "model_module_version": "1.5.0", + "model_name": "DescriptionStyleModel", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", @@ -8160,1363 +9312,10 @@ "description_width": "" } }, - "3703041a499c426bb427ee008c81cde5": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_4b22bbacb995425fb32a2368f3685a92", - "IPY_MODEL_49a66eeb9ef74de5ab8904fd90eb7558", - "IPY_MODEL_08f9d125018b41c582a0fa1e234315f9" - ], - "layout": "IPY_MODEL_736c770230644894b85dbc34bd8f1d52" - } - }, - "4b22bbacb995425fb32a2368f3685a92": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_b67cbbf32f844a19b219be612d5038c9", - "placeholder": "​", - "style": "IPY_MODEL_774b513d64524ac7823a2cf13efa8d41", - "value": "vocab.txt: 100%" - } - }, - "49a66eeb9ef74de5ab8904fd90eb7558": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1e56da93bcf64ff490416d2b66cd3dc0", - "max": 231508, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_b7e35038ce344110b785753b655130f5", - "value": 231508 - } - }, - "08f9d125018b41c582a0fa1e234315f9": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_5472af91737446f4a4a2d92a3f684a45", - "placeholder": "​", - "style": "IPY_MODEL_9fb4368802da4a5a8101ba200d98403a", - "value": " 232k/232k [00:00<00:00, 3.18MB/s]" - } - }, - "736c770230644894b85dbc34bd8f1d52": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b67cbbf32f844a19b219be612d5038c9": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "774b513d64524ac7823a2cf13efa8d41": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "1e56da93bcf64ff490416d2b66cd3dc0": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b7e35038ce344110b785753b655130f5": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "5472af91737446f4a4a2d92a3f684a45": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9fb4368802da4a5a8101ba200d98403a": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "2e713bcc372e48b2a006558db4d1df68": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_1a277abd5ea44253bc6894bef258b52b", - "IPY_MODEL_b3eedd82e7da4ce8b3ded70e49a2afd0", - "IPY_MODEL_6f5c18cb8002471f8b3764effee37324" - ], - "layout": "IPY_MODEL_3bebac362b344e8d9103c5011613f1ea" - } - }, - "1a277abd5ea44253bc6894bef258b52b": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_670905a55b19458da69f83c8bcd511d1", - "placeholder": "​", - "style": "IPY_MODEL_ff54451a48394faaaa9d8cdb690d0718", - "value": "tokenizer.json: 100%" - } - }, - "b3eedd82e7da4ce8b3ded70e49a2afd0": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_36b5bc19b2d0407f8ab28ff0da2ce12d", - "max": 466247, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_879e48d9a9e04183903d94ffe98313d2", - "value": 466247 - } - }, - "6f5c18cb8002471f8b3764effee37324": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_abce503d70594c2ca9afdc47847c125b", - "placeholder": "​", - "style": "IPY_MODEL_028e291ee53947bbbbc4bfb68c695f5f", - "value": " 466k/466k [00:00<00:00, 3.52MB/s]" - } - }, - "3bebac362b344e8d9103c5011613f1ea": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "670905a55b19458da69f83c8bcd511d1": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, "ff54451a48394faaaa9d8cdb690d0718": { "model_module": "@jupyter-widgets/controls", + "model_module_version": "1.5.0", "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "36b5bc19b2d0407f8ab28ff0da2ce12d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "879e48d9a9e04183903d94ffe98313d2": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "abce503d70594c2ca9afdc47847c125b": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "028e291ee53947bbbbc4bfb68c695f5f": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "a530662719374c95a9bef12e59e28c85": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_bffc0f4b12f141398535990709fd4f2c", - "IPY_MODEL_04804c74e1dd43449d5f758cf5d0ba5e", - "IPY_MODEL_95a506c3007c4525b01ee4e1600d671b" - ], - "layout": "IPY_MODEL_a0d6b0caeb2340fe96c8f5569e3d3ae4" - } - }, - "bffc0f4b12f141398535990709fd4f2c": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_30798f87a8b848d783fdacd71af5dc04", - "placeholder": "​", - "style": "IPY_MODEL_07ce54c75e76488ba4019a20b3707061", - "value": "special_tokens_map.json: 100%" - } - }, - "04804c74e1dd43449d5f758cf5d0ba5e": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_f023175de68445f98a6b01bb40ccdc6d", - "max": 112, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_7389b79a0ff44cd68c7866995d728023", - "value": 112 - } - }, - "95a506c3007c4525b01ee4e1600d671b": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_8e2b70ffe4eb4974bd6393fcc1292267", - "placeholder": "​", - "style": "IPY_MODEL_13eee164dc534424acb9dc9ee37a9465", - "value": " 112/112 [00:00<00:00, 8.09kB/s]" - } - }, - "a0d6b0caeb2340fe96c8f5569e3d3ae4": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "30798f87a8b848d783fdacd71af5dc04": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "07ce54c75e76488ba4019a20b3707061": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "f023175de68445f98a6b01bb40ccdc6d": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "7389b79a0ff44cd68c7866995d728023": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "8e2b70ffe4eb4974bd6393fcc1292267": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "13eee164dc534424acb9dc9ee37a9465": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "722a7fe16af3422585a20c651345cfa4": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HBoxModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_f5596c1c9c4d42f3bc171961f9582eff", - "IPY_MODEL_85d66e615b5742e78657b1e60c75fc72", - "IPY_MODEL_731c02dc5dd446c3b22765575148e256" - ], - "layout": "IPY_MODEL_254ce460ce244c99a5afe39d5d51f6b7" - } - }, - "f5596c1c9c4d42f3bc171961f9582eff": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_4cf1dc345ace4da59f978f661487f975", - "placeholder": "​", - "style": "IPY_MODEL_8f30fca71bf24e5ca26e17c2321f893c", - "value": "1_Pooling/config.json: 100%" - } - }, - "85d66e615b5742e78657b1e60c75fc72": { - "model_module": "@jupyter-widgets/controls", - "model_name": "FloatProgressModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_dd85d37dd1d14c7ea4592f8e11b2d2c8", - "max": 190, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_3cb06377e4454f009d6b2aa7aa6ff0a9", - "value": 190 - } - }, - "731c02dc5dd446c3b22765575148e256": { - "model_module": "@jupyter-widgets/controls", - "model_name": "HTMLModel", - "model_module_version": "1.5.0", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_4502477db4d948e693012364c2dcb370", - "placeholder": "​", - "style": "IPY_MODEL_52fe404ec9c14db2a7279b4c154eef3d", - "value": " 190/190 [00:00<00:00, 12.8kB/s]" - } - }, - "254ce460ce244c99a5afe39d5d51f6b7": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4cf1dc345ace4da59f978f661487f975": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "8f30fca71bf24e5ca26e17c2321f893c": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "dd85d37dd1d14c7ea4592f8e11b2d2c8": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "3cb06377e4454f009d6b2aa7aa6ff0a9": { - "model_module": "@jupyter-widgets/controls", - "model_name": "ProgressStyleModel", - "model_module_version": "1.5.0", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "4502477db4d948e693012364c2dcb370": { - "model_module": "@jupyter-widgets/base", - "model_name": "LayoutModel", - "model_module_version": "1.2.0", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "52fe404ec9c14db2a7279b4c154eef3d": { - "model_module": "@jupyter-widgets/controls", - "model_name": "DescriptionStyleModel", - "model_module_version": "1.5.0", "state": { "_model_module": "@jupyter-widgets/controls", "_model_module_version": "1.5.0", From 82d942b501fdcce237544b04bd99f6351bf083f1 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 13:58:17 -0800 Subject: [PATCH 078/107] Foo --- ...Llama_Stack_Building_AI_Applications.ipynb | 24 +++++-------------- 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index 4c3f680fd..d86fcd673 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -1112,8 +1112,6 @@ "source": [ "import os\n", "\n", - "os.environ[\"TOGETHER_API_KEY\"] = \"2d8335559c046920fd3ccffae6d7057353b289d6272d5e979621457eb330e82b\"\n", - "os.environ[\"TAVILY_SEARCH_API_KEY\"] = \"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\"\n", "try:\n", " from google.colab import userdata\n", " os.environ['TOGETHER_API_KEY'] = userdata.get('TOGETHER_API_KEY')\n", @@ -3242,7 +3240,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "4iCO59kP20Zs", "metadata": { "colab": { @@ -3256,25 +3254,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mN\u001b[0m\u001b[36mBA\u001b[0m\u001b[36m Western\u001b[0m\u001b[36m Conference\u001b[0m\u001b[36m Finals\u001b[0m\u001b[36m \u001b[0m\u001b[36m202\u001b[0m\u001b[36m4\u001b[0m\u001b[36m teams\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n", - "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\u001b[0m\n", - "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.9310187, \"raw_content\": null}, {\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.8914433, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.8884594, \"raw_content\": null}, {\"title\": \"NBA Conference Finals Schedule: Full List of Games & Results\", \"url\": \"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\", \"content\": \"The 2024 NBA conference finals matchups are set. Here's the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\", \"score\": 0.85008353, \"raw_content\": null}, {\"title\": \"2024 NBA Western Conference playoff bracket - Basketnews.com\", \"url\": \"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\", \"content\": \"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\", \"score\": 0.8479807, \"raw_content\": null}]}\u001b[0m\n", - "\u001b[33minference> \u001b[0m\u001b[33mThe\u001b[0m\u001b[33m teams\u001b[0m\u001b[33m that\u001b[0m\u001b[33m played\u001b[0m\u001b[33m in\u001b[0m\u001b[33m the\u001b[0m\u001b[33m NBA\u001b[0m\u001b[33m Western\u001b[0m\u001b[33m Conference\u001b[0m\u001b[33m Finals\u001b[0m\u001b[33m of\u001b[0m\u001b[33m \u001b[0m\u001b[33m202\u001b[0m\u001b[33m4\u001b[0m\u001b[33m were\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Dallas\u001b[0m\u001b[33m Mavericks\u001b[0m\u001b[33m and\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Minnesota\u001b[0m\u001b[33m Timber\u001b[0m\u001b[33mw\u001b[0m\u001b[33molves\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[33mLet\u001b[0m\u001b[33m me\u001b[0m\u001b[33m check\u001b[0m\u001b[33m the\u001b[0m\u001b[33m latest\u001b[0m\u001b[33m sports\u001b[0m\u001b[33m news\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[30m\u001b[0m\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mBill\u001b[0m\u001b[36m Cosby\u001b[0m\u001b[36m South\u001b[0m\u001b[36m Park\u001b[0m\u001b[36m episode\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'Bill Cosby South Park episode'}\u001b[0m\n", - "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Bill Cosby South Park episode\", \"top_k\": [{\"title\": \"Bill Cosby and Taylor Swift Duet - South Park Studios\", \"url\": \"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\", \"content\": \"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift's rendition of \\\"It's Snowing Out There\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman's plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\", \"score\": 0.685971, \"raw_content\": null}, {\"title\": \"Bill Cosby is Here to See You - South Park Studios US\", \"url\": \"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\", \"content\": \"01:56 It's Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde's performance and calls the Record Producer to try and fix it. 01:24 Lorde's Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac's hologram. 01:37 I've Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\", \"score\": 0.6643884, \"raw_content\": null}, {\"title\": \"Bill Cosby (android) | South Park Character ... - South Park Studios US\", \"url\": \"https://southpark.cc.com/wiki/Bill_Cosby_(android)\", \"content\": \"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman's Dawson's Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\"Bill Cosby\\\" is really VSM471, an android or cyborg of some kind engineered by 'hoomans' in the distant future. He fails in his initial missions to infiltrate South Park Elementary's 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski's aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\", \"score\": 0.5052006, \"raw_content\": null}, {\"title\": \"'South Park' takes on Cosby, police, 2014 | CNN\", \"url\": \"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\", \"content\": \"\\u2018South Park\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\u00a0\\u2014\\u00a0 \\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\u201d wrote Brent Veale. \\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\", \"score\": 0.45391592, \"raw_content\": null}, {\"title\": \"Trapper Keeper (South Park) - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\", \"content\": \"\\\"Trapper Keeper\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman's new Trapper Keeper, while Mr. Garrison's kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election's outcome.[2] \\\"Trapper Keeper\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\"Trapper Keeper\\\" Full episode at South Park Studios\", \"score\": 0.3839421, \"raw_content\": null}]}\u001b[0m\n", - "\u001b[33minference> \u001b[0m\u001b[33mBill\u001b[0m\u001b[33m Cosby\u001b[0m\u001b[33m (\u001b[0m\u001b[33mBS\u001b[0m\u001b[33mM\u001b[0m\u001b[33m-\u001b[0m\u001b[33m471\u001b[0m\u001b[33m)\u001b[0m\u001b[33m first\u001b[0m\u001b[33m appears\u001b[0m\u001b[33m in\u001b[0m\u001b[33m Season\u001b[0m\u001b[33m \u001b[0m\u001b[33m4\u001b[0m\u001b[33m,\u001b[0m\u001b[33m Episode\u001b[0m\u001b[33m \u001b[0m\u001b[33m12\u001b[0m\u001b[33m of\u001b[0m\u001b[33m South\u001b[0m\u001b[33m Park\u001b[0m\u001b[33m,\u001b[0m\u001b[33m titled\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mTr\u001b[0m\u001b[33mapper\u001b[0m\u001b[33m Keeper\u001b[0m\u001b[33m\".\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Bill Cosby South Park episode\", \"top_k\": [{\"title\": \"Bill Cosby and Taylor Swift Duet - South Park Studios\", \"url\": \"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\", \"content\": \"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift's rendition of \\\"It's Snowing Out There\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman's plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\", \"score\": 0.685971, \"raw_content\": null}, {\"title\": \"Bill Cosby is Here to See You - South Park Studios US\", \"url\": \"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\", \"content\": \"01:56 It's Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde's performance and calls the Record Producer to try and fix it. 01:24 Lorde's Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac's hologram. 01:37 I've Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\", \"score\": 0.6643884, \"raw_content\": null}, {\"title\": \"Bill Cosby (android) | South Park Character ... - South Park Studios US\", \"url\": \"https://southpark.cc.com/wiki/Bill_Cosby_(android)\", \"content\": \"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman's Dawson's Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\"Bill Cosby\\\" is really VSM471, an android or cyborg of some kind engineered by 'hoomans' in the distant future. He fails in his initial missions to infiltrate South Park Elementary's 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski's aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\", \"score\": 0.5052006, \"raw_content\": null}, {\"title\": \"Trapper Keeper (South Park) - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\", \"content\": \"\\\"Trapper Keeper\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman's new Trapper Keeper, while Mr. Garrison's kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election's outcome.[2] \\\"Trapper Keeper\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\"Trapper Keeper\\\" Full episode at South Park Studios\", \"score\": 0.3839421, \"raw_content\": null}, {\"title\": \"Bill Cosby | South Park Archives | Fandom\", \"url\": \"https://southpark.fandom.com/wiki/Bill_Cosby\", \"content\": \"SIGN IN CHARACTERS SIGN IN Explore EXPLORE CHARACTERS SIGN IN TO EDIT Character Information For other uses, see Bill (Disambiguation). Bill Cosby is elderly, having gray hair as well as various facial wrinkles. More Information: Criminal Celebrities More Information: Movie Celebrities Minor Characters from Season Four More information: List of Minor Characters from Season Four | Season Four Community content is available under CC-BY-SA unless otherwise noted. EXPLORE PROPERTIES FOLLOW US Terms of Use Global Sitemap Local Sitemap Follow on IG\", \"score\": 0.34707275, \"raw_content\": null}]}\u001b[0m\n", + "\u001b[33minference> \u001b[0m\u001b[33mBill\u001b[0m\u001b[33m Cosby\u001b[0m\u001b[33m (\u001b[0m\u001b[33mBS\u001b[0m\u001b[33mM\u001b[0m\u001b[33m-\u001b[0m\u001b[33m471\u001b[0m\u001b[33m)\u001b[0m\u001b[33m first\u001b[0m\u001b[33m appears\u001b[0m\u001b[33m in\u001b[0m\u001b[33m the\u001b[0m\u001b[33m episode\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mTr\u001b[0m\u001b[33mapper\u001b[0m\u001b[33m Keeper\u001b[0m\u001b[33m\"\u001b[0m\u001b[33m (\u001b[0m\u001b[33mSeason\u001b[0m\u001b[33m \u001b[0m\u001b[33m4\u001b[0m\u001b[33m,\u001b[0m\u001b[33m Episode\u001b[0m\u001b[33m \u001b[0m\u001b[33m12\u001b[0m\u001b[33m)\u001b[0m\u001b[33m of\u001b[0m\u001b[33m South\u001b[0m\u001b[33m Park\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[30m\u001b[0m\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mAndrew\u001b[0m\u001b[36m Tate\u001b[0m\u001b[36m kick\u001b[0m\u001b[36mboxing\u001b[0m\u001b[36m name\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'Andrew Tate kickboxing name'}\u001b[0m\n", - "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Andrew Tate kickboxing name\", \"top_k\": [{\"title\": \"50 Facts About Andrew Tate - Facts.net\", \"url\": \"https://facts.net/andrew-tate-facts/\", \"content\": \"Full Name: Andrew Tate's full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\", \"score\": 0.8967681, \"raw_content\": null}, {\"title\": \"The Life Of Andrew Tate (By Andrew Tate Himself)\", \"url\": \"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\", \"content\": \"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate's Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\", \"score\": 0.8795718, \"raw_content\": null}, {\"title\": \"Andrew Tate kickboxing record: How many championships ... - FirstSportz\", \"url\": \"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\", \"content\": \"Andrew Tate's Kickboxing career. During his kickboxing career, he used the nickname \\\"King Cobra,\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\", \"score\": 0.8752871, \"raw_content\": null}, {\"title\": \"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\", \"url\": \"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\", \"content\": \"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\", \"score\": 0.7992077, \"raw_content\": null}, {\"title\": \"About Andrew Tate: A Journey from Champion to Controversy\", \"url\": \"https://reachmorpheus.com/andrew-tate/\", \"content\": \"Andrew Tate's kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\", \"score\": 0.6490677, \"raw_content\": null}]}\u001b[0m\n", - "\u001b[33minference> \u001b[0m\u001b[33mAndrew\u001b[0m\u001b[33m Tate\u001b[0m\u001b[33m's\u001b[0m\u001b[33m kick\u001b[0m\u001b[33mboxing\u001b[0m\u001b[33m name\u001b[0m\u001b[33m is\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mC\u001b[0m\u001b[33mobra\u001b[0m\u001b[33m Tate\u001b[0m\u001b[33m\"\u001b[0m\u001b[33m or\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mKing\u001b[0m\u001b[33m Cobra\u001b[0m\u001b[33m\".\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Andrew Tate kickboxing name\", \"top_k\": [{\"title\": \"50 Facts About Andrew Tate - Facts.net\", \"url\": \"https://facts.net/andrew-tate-facts/\", \"content\": \"Full Name: Andrew Tate's full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\", \"score\": 0.8967681, \"raw_content\": null}, {\"title\": \"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\", \"url\": \"https://biographywallah.com/andrew-tate-biography/\", \"content\": \"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old (as of 2024)NationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\", \"score\": 0.80698997, \"raw_content\": null}, {\"title\": \"The Life Of Andrew Tate (By Andrew Tate Himself ... - Sidekick Boxing\", \"url\": \"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\", \"content\": \"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\u2019s life has been full of adventure.\", \"score\": 0.7817479, \"raw_content\": null}, {\"title\": \"50 Facts About Andrew Tate\", \"url\": \"https://facts.net/celebrity/50-facts-about-andrew-tate/\", \"content\": \"50 Facts About Andrew Tate - Facts.net Everything Else Facts Everything Else Facts 50 Facts About Andrew Tate Known for his kickboxing prowess, internet fame, and polarizing views, Tate's life is a blend of high achievements and significant legal troubles. Andrew Tate, a kickboxing champion turned internet personality, faced controversy and legal issues, showcasing the complexities of fame and the impact of social media influence on personal reputation. Andrew Tate's kickboxing career is one of his most notable achievements. Andrew Tate, a former professional kickboxer turned internet personality, has made waves online with his controversial opinions and business ventures. 20 Tristan Tate Facts A Deep Dive into the Life of a Controversial Figure 47 Facts About Larenz Tate More Facts\", \"score\": 0.61834323, \"raw_content\": null}, {\"title\": \"Andrew Tate Kickboxing Record: Legacy of King Cobra\", \"url\": \"https://stagbite.com/andrew-tate-kickboxing-record/\", \"content\": \"Andrew Tate Kickboxing Record: Legacy Of King Cobra \\u2013 Stagbite Andrew Tate Kickboxing Record: Legacy of King Cobra Andrew Tate Kickboxing Record: Legacy of King Cobra Over the course of his career, Andrew Tate amassed an impressive kickboxing record of 76 wins and 9 losses, with 23 of those victories coming via knockout or technical knockout. Andrew Tate\\u2019s Kickboxing Record What is Andrew Tate\\u2019s kickboxing record? Andrew Tate has a kickboxing record of 76 wins and 9 losses, with 23 wins coming via knockout or technical knockout. What titles did Andrew Tate win during his kickboxing career? We talk, write, and share some of the best Internet stories on Entertainment, Culture, Travel, Food, Books along with the social media trends & viral bees.\", \"score\": 0.59796065, \"raw_content\": null}]}\u001b[0m\n", + "\u001b[33minference> \u001b[0m\u001b[33mAndrew\u001b[0m\u001b[33m Tate\u001b[0m\u001b[33m's\u001b[0m\u001b[33m kick\u001b[0m\u001b[33mboxing\u001b[0m\u001b[33m name\u001b[0m\u001b[33m is\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mKing\u001b[0m\u001b[33m Cobra\u001b[0m\u001b[33m.\"\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[30m\u001b[0m" ] } From 8738c3e5a7d278d33be139bddac1262aedad850d Mon Sep 17 00:00:00 2001 From: Botao Chen Date: Wed, 22 Jan 2025 15:04:05 -0800 Subject: [PATCH 079/107] fix experimental-post-training template (#842) ## What does this PR do? For the completion of https://github.com/meta-llama/llama-stack/pull/835 ## Test Plan llama stack build --template experimental-post-training --image-type conda llama stack run llama_stack/templates/experimental-post-training/run.yaml --- llama_stack/templates/experimental-post-training/build.yaml | 2 +- llama_stack/templates/experimental-post-training/run.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/llama_stack/templates/experimental-post-training/build.yaml b/llama_stack/templates/experimental-post-training/build.yaml index 618e8ff97..c146d1b37 100644 --- a/llama_stack/templates/experimental-post-training/build.yaml +++ b/llama_stack/templates/experimental-post-training/build.yaml @@ -22,7 +22,7 @@ distribution_spec: - inline::meta-reference safety: - inline::llama-guard - memory: + vector_io: - inline::faiss tool_runtime: - remote::brave-search diff --git a/llama_stack/templates/experimental-post-training/run.yaml b/llama_stack/templates/experimental-post-training/run.yaml index 14323573c..75d103c9f 100644 --- a/llama_stack/templates/experimental-post-training/run.yaml +++ b/llama_stack/templates/experimental-post-training/run.yaml @@ -7,7 +7,7 @@ apis: - datasetio - eval - inference -- memory +- vector_io - safety - scoring - telemetry From deab4f57ddf2187065529aac269f39b79653ef14 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Wed, 22 Jan 2025 15:27:09 -0800 Subject: [PATCH 080/107] Improved report generation for providers (#844) # What does this PR do? Automates the model list check by querying the distro. Added support for both remote hosted and templates. ## Test Plan Run on a remote hosted distro via `LLAMA_STACK_BASE_URL="https://llamastack-preview.fireworks.ai" pytest -s -v tests/client-sdk --report` Run on a template via `LLAMA_STACK_CONFIG=fireworks pytest -s -v tests/client-sdk --report` --- .../fireworks/remote-hosted-report.md | 45 +++++ llama_stack/templates/fireworks/report.md | 34 ++-- tests/client-sdk/report.py | 157 +++++++++--------- 3 files changed, 142 insertions(+), 94 deletions(-) create mode 100644 llama_stack/templates/fireworks/remote-hosted-report.md diff --git a/llama_stack/templates/fireworks/remote-hosted-report.md b/llama_stack/templates/fireworks/remote-hosted-report.md new file mode 100644 index 000000000..fb338ba13 --- /dev/null +++ b/llama_stack/templates/fireworks/remote-hosted-report.md @@ -0,0 +1,45 @@ +# Report for fireworks distribution + +## Supported Models: +| Model Descriptor | fireworks | +|:---|:---| +| meta-llama/Llama-3-8B-Instruct | ❌ | +| meta-llama/Llama-3-70B-Instruct | ❌ | +| meta-llama/Llama-3.1-8B-Instruct | ❌ | +| meta-llama/Llama-3.1-70B-Instruct | ❌ | +| meta-llama/Llama-3.1-405B-Instruct-FP8 | ❌ | +| meta-llama/Llama-3.2-1B-Instruct | ❌ | +| meta-llama/Llama-3.2-3B-Instruct | ❌ | +| meta-llama/Llama-3.2-11B-Vision-Instruct | ❌ | +| meta-llama/Llama-3.2-90B-Vision-Instruct | ❌ | +| meta-llama/Llama-3.3-70B-Instruct | ❌ | +| meta-llama/Llama-Guard-3-11B-Vision | ❌ | +| meta-llama/Llama-Guard-3-1B | ❌ | +| meta-llama/Llama-Guard-3-8B | ❌ | +| meta-llama/Llama-Guard-2-8B | ❌ | + +## Inference: +| Model | API | Capability | Test | Status | +|:----- |:-----|:-----|:-----|:-----| +| Text | /chat_completion | streaming | test_text_chat_completion_streaming | ❌ | +| Vision | /chat_completion | streaming | test_image_chat_completion_streaming | ❌ | +| Vision | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | ❌ | +| Text | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ❌ | +| Text | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ❌ | +| Text | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ❌ | +| Text | /completion | streaming | test_text_completion_streaming | ❌ | +| Text | /completion | non_streaming | test_text_completion_non_streaming | ❌ | +| Text | /completion | structured_output | test_text_completion_structured_output | ❌ | + +## Memory: +| API | Capability | Test | Status | +|:-----|:-----|:-----|:-----| +| /insert, /query | inline | test_memory_bank_insert_inline_and_query | ❌ | +| /insert, /query | url | test_memory_bank_insert_from_url_and_query | ❌ | + +## Agents: +| API | Capability | Test | Status | +|:-----|:-----|:-----|:-----| +| create_agent_turn | rag | test_rag_agent | ❌ | +| create_agent_turn | custom_tool | test_custom_tool | ❌ | +| create_agent_turn | code_execution | test_code_execution | ❌ | diff --git a/llama_stack/templates/fireworks/report.md b/llama_stack/templates/fireworks/report.md index ac6fab6eb..55efec0f5 100644 --- a/llama_stack/templates/fireworks/report.md +++ b/llama_stack/templates/fireworks/report.md @@ -3,20 +3,20 @@ ## Supported Models: | Model Descriptor | fireworks | |:---|:---| -| Llama-3-8B-Instruct | ❌ | -| Llama-3-70B-Instruct | ❌ | -| Llama3.1-8B-Instruct | ✅ | -| Llama3.1-70B-Instruct | ✅ | -| Llama3.1-405B-Instruct | ✅ | -| Llama3.2-1B-Instruct | ✅ | -| Llama3.2-3B-Instruct | ✅ | -| Llama3.2-11B-Vision-Instruct | ✅ | -| Llama3.2-90B-Vision-Instruct | ✅ | -| Llama3.3-70B-Instruct | ✅ | -| Llama-Guard-3-11B-Vision | ✅ | -| Llama-Guard-3-1B | ❌ | -| Llama-Guard-3-8B | ✅ | -| Llama-Guard-2-8B | ❌ | +| meta-llama/Llama-3-8B-Instruct | ❌ | +| meta-llama/Llama-3-70B-Instruct | ❌ | +| meta-llama/Llama-3.1-8B-Instruct | ✅ | +| meta-llama/Llama-3.1-70B-Instruct | ✅ | +| meta-llama/Llama-3.1-405B-Instruct-FP8 | ✅ | +| meta-llama/Llama-3.2-1B-Instruct | ✅ | +| meta-llama/Llama-3.2-3B-Instruct | ✅ | +| meta-llama/Llama-3.2-11B-Vision-Instruct | ✅ | +| meta-llama/Llama-3.2-90B-Vision-Instruct | ✅ | +| meta-llama/Llama-3.3-70B-Instruct | ✅ | +| meta-llama/Llama-Guard-3-11B-Vision | ✅ | +| meta-llama/Llama-Guard-3-1B | ❌ | +| meta-llama/Llama-Guard-3-8B | ✅ | +| meta-llama/Llama-Guard-2-8B | ❌ | ## Inference: | Model | API | Capability | Test | Status | @@ -34,12 +34,12 @@ ## Memory: | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| -| /insert, /query | inline | test_memory_bank_insert_inline_and_query | ❌ | -| /insert, /query | url | test_memory_bank_insert_from_url_and_query | ❌ | +| /insert, /query | inline | test_memory_bank_insert_inline_and_query | ✅ | +| /insert, /query | url | test_memory_bank_insert_from_url_and_query | ✅ | ## Agents: | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| -| create_agent_turn | rag | test_rag_agent | ❌ | +| create_agent_turn | rag | test_rag_agent | ✅ | | create_agent_turn | custom_tool | test_custom_tool | ✅ | | create_agent_turn | code_execution | test_code_execution | ❌ | diff --git a/tests/client-sdk/report.py b/tests/client-sdk/report.py index 22aa98935..5a291f1af 100644 --- a/tests/client-sdk/report.py +++ b/tests/client-sdk/report.py @@ -5,88 +5,87 @@ # the root directory of this source tree. +import importlib import os from collections import defaultdict from pathlib import Path +from urllib.parse import urlparse import pytest -from llama_models.datatypes import CoreModelId -from llama_models.sku_list import all_registered_models + +from llama_models.sku_list import ( + llama3_1_instruct_models, + llama3_2_instruct_models, + llama3_3_instruct_models, + llama3_instruct_models, + safety_models, +) from llama_stack.distribution.library_client import LlamaStackAsLibraryClient +from llama_stack.providers.tests.env import get_env_or_fail + +from llama_stack_client import LlamaStackClient from metadata import API_MAPS from pytest import CollectReport +from termcolor import cprint -SUPPORTED_MODELS = { - "ollama": set( - [ - CoreModelId.llama3_1_8b_instruct.value, - CoreModelId.llama3_1_8b_instruct.value, - CoreModelId.llama3_1_70b_instruct.value, - CoreModelId.llama3_1_70b_instruct.value, - CoreModelId.llama3_1_405b_instruct.value, - CoreModelId.llama3_1_405b_instruct.value, - CoreModelId.llama3_2_1b_instruct.value, - CoreModelId.llama3_2_1b_instruct.value, - CoreModelId.llama3_2_3b_instruct.value, - CoreModelId.llama3_2_3b_instruct.value, - CoreModelId.llama3_2_11b_vision_instruct.value, - CoreModelId.llama3_2_11b_vision_instruct.value, - CoreModelId.llama3_2_90b_vision_instruct.value, - CoreModelId.llama3_2_90b_vision_instruct.value, - CoreModelId.llama3_3_70b_instruct.value, - CoreModelId.llama_guard_3_8b.value, - CoreModelId.llama_guard_3_1b.value, - ] - ), - "fireworks": set( - [ - CoreModelId.llama3_1_8b_instruct.value, - CoreModelId.llama3_1_70b_instruct.value, - CoreModelId.llama3_1_405b_instruct.value, - CoreModelId.llama3_2_1b_instruct.value, - CoreModelId.llama3_2_3b_instruct.value, - CoreModelId.llama3_2_11b_vision_instruct.value, - CoreModelId.llama3_2_90b_vision_instruct.value, - CoreModelId.llama3_3_70b_instruct.value, - CoreModelId.llama_guard_3_8b.value, - CoreModelId.llama_guard_3_11b_vision.value, - ] - ), - "together": set( - [ - CoreModelId.llama3_1_8b_instruct.value, - CoreModelId.llama3_1_70b_instruct.value, - CoreModelId.llama3_1_405b_instruct.value, - CoreModelId.llama3_2_3b_instruct.value, - CoreModelId.llama3_2_11b_vision_instruct.value, - CoreModelId.llama3_2_90b_vision_instruct.value, - CoreModelId.llama3_3_70b_instruct.value, - CoreModelId.llama_guard_3_8b.value, - CoreModelId.llama_guard_3_11b_vision.value, - ] - ), -} +def featured_models_repo_names(): + models = [ + *llama3_instruct_models(), + *llama3_1_instruct_models(), + *llama3_2_instruct_models(), + *llama3_3_instruct_models(), + *safety_models(), + ] + return [model.huggingface_repo for model in models if not model.variant] class Report: def __init__(self): - config_file = os.environ.get("LLAMA_STACK_CONFIG") - if not config_file: - raise ValueError( - "Currently we only support generating report for LlamaStackClientLibrary distributions" + if os.environ.get("LLAMA_STACK_CONFIG"): + config_path_or_template_name = get_env_or_fail("LLAMA_STACK_CONFIG") + if config_path_or_template_name.endswith(".yaml"): + config_path = Path(config_path_or_template_name) + else: + config_path = Path( + importlib.resources.files("llama_stack") + / f"templates/{config_path_or_template_name}/run.yaml" + ) + if not config_path.exists(): + raise ValueError(f"Config file {config_path} does not exist") + self.output_path = Path(config_path.parent / "report.md") + self.client = LlamaStackAsLibraryClient( + config_path_or_template_name, + provider_data=None, + skip_logger_removal=True, ) - config_path = Path(config_file) - self.output_path = Path(config_path.parent / "report.md") - self.client = LlamaStackAsLibraryClient( - config_file, - provider_data=None, - skip_logger_removal=True, - ) - self.image_name = self.client.async_client.config.image_name + self.client.initialize() + self.image_name = self.client.async_client.config.image_name + elif os.environ.get("LLAMA_STACK_BASE_URL"): + url = get_env_or_fail("LLAMA_STACK_BASE_URL") + hostname = urlparse(url).netloc + domain = hostname.split(".")[-2] + self.image_name = domain + + self.client = LlamaStackClient( + base_url=url, + provider_data=None, + ) + # We assume that the domain maps to a template + # i.e. https://llamastack-preview.fireworks.ai --> "fireworks" template + # and add report in that directory + output_dir = Path( + importlib.resources.files("llama_stack") / f"templates/{domain}/" + ) + if not output_dir.exists(): + raise ValueError(f"Output dir {output_dir} does not exist") + self.output_path = Path(output_dir / "remote-hosted-report.md") + else: + raise ValueError("LLAMA_STACK_CONFIG or LLAMA_STACK_BASE_URL must be set") + self.report_data = defaultdict(dict) # test function -> test nodeid self.test_data = dict() @@ -105,7 +104,7 @@ class Report: def pytest_sessionfinish(self, session): report = [] report.append(f"# Report for {self.image_name} distribution") - report.append("\n## Supported Models: ") + report.append("\n## Supported Models:") header = f"| Model Descriptor | {self.image_name} |" dividor = "|:---|:---|" @@ -114,21 +113,23 @@ class Report: report.append(dividor) rows = [] - for model in all_registered_models(): - if ( - "Instruct" not in model.core_model_id.value - and "Guard" not in model.core_model_id.value - ) or (model.variant): - continue - row = f"| {model.core_model_id.value} |" - if model.core_model_id.value in SUPPORTED_MODELS[self.image_name]: + + try: + supported_models = {m.identifier for m in self.client.models.list()} + except Exception as e: + cprint(f"Error getting models: {e}", "red") + supported_models = set() + + for m_name in featured_models_repo_names(): + row = f"| {m_name} |" + if m_name in supported_models: row += " ✅ |" else: row += " ❌ |" rows.append(row) report.extend(rows) - report.append("\n## Inference: ") + report.append("\n## Inference:") test_table = [ "| Model | API | Capability | Test | Status |", "|:----- |:-----|:-----|:-----|:-----|", @@ -150,7 +151,7 @@ class Report: for api_group in ["memory", "agents"]: api_capitalized = api_group.capitalize() - report.append(f"\n## {api_capitalized}: ") + report.append(f"\n## {api_capitalized}:") test_table = [ "| API | Capability | Test | Status |", "|:-----|:-----|:-----|:-----|", @@ -164,9 +165,11 @@ class Report: f"| {api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |" ) report.extend(test_table) + output_file = self.output_path - output_file.write_text("\n".join(report)) - print(f"\nReport generated: {output_file.absolute()}") + text = "\n".join(report) + "\n" + output_file.write_text(text) + cprint(f"\nReport generated: {output_file.absolute()}", "green") def pytest_runtest_makereport(self, item, call): func_name = getattr(item, "originalname", item.name) From 494e969f8d438b70475c9307a8ed1cd1b9da1c27 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 14:22:10 -0800 Subject: [PATCH 081/107] add a bunch of NBVAL SKIPs to unblock ugh --- ...Llama_Stack_Building_AI_Applications.ipynb | 198 +++++++++--------- 1 file changed, 101 insertions(+), 97 deletions(-) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index d86fcd673..daf37ab53 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -3240,7 +3240,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 18, "id": "4iCO59kP20Zs", "metadata": { "colab": { @@ -3254,21 +3254,24 @@ "name": "stdout", "output_type": "stream", "text": [ - "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[33mLet\u001b[0m\u001b[33m me\u001b[0m\u001b[33m check\u001b[0m\u001b[33m the\u001b[0m\u001b[33m latest\u001b[0m\u001b[33m sports\u001b[0m\u001b[33m news\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mN\u001b[0m\u001b[36mBA\u001b[0m\u001b[36m Western\u001b[0m\u001b[36m Conference\u001b[0m\u001b[36m Finals\u001b[0m\u001b[36m \u001b[0m\u001b[36m202\u001b[0m\u001b[36m4\u001b[0m\u001b[36m teams\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n", + "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\u001b[0m\n", + "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.9310187, \"raw_content\": null}, {\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.8914433, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.8884594, \"raw_content\": null}, {\"title\": \"2024 NBA Western Conference playoff bracket - Basketnews.com\", \"url\": \"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\", \"content\": \"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\", \"score\": 0.8479807, \"raw_content\": null}, {\"title\": \"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\", \"url\": \"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\", \"content\": \"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\", \"score\": 0.81979275, \"raw_content\": null}]}\u001b[0m\n", + "\u001b[33minference> \u001b[0m\u001b[33mThe\u001b[0m\u001b[33m teams\u001b[0m\u001b[33m that\u001b[0m\u001b[33m played\u001b[0m\u001b[33m in\u001b[0m\u001b[33m the\u001b[0m\u001b[33m NBA\u001b[0m\u001b[33m Western\u001b[0m\u001b[33m Conference\u001b[0m\u001b[33m Finals\u001b[0m\u001b[33m of\u001b[0m\u001b[33m \u001b[0m\u001b[33m202\u001b[0m\u001b[33m4\u001b[0m\u001b[33m were\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Dallas\u001b[0m\u001b[33m Mavericks\u001b[0m\u001b[33m and\u001b[0m\u001b[33m the\u001b[0m\u001b[33m Minnesota\u001b[0m\u001b[33m Timber\u001b[0m\u001b[33mw\u001b[0m\u001b[33molves\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[30m\u001b[0m\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mBill\u001b[0m\u001b[36m Cosby\u001b[0m\u001b[36m South\u001b[0m\u001b[36m Park\u001b[0m\u001b[36m episode\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'Bill Cosby South Park episode'}\u001b[0m\n", - "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Bill Cosby South Park episode\", \"top_k\": [{\"title\": \"Bill Cosby and Taylor Swift Duet - South Park Studios\", \"url\": \"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\", \"content\": \"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift's rendition of \\\"It's Snowing Out There\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman's plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\", \"score\": 0.685971, \"raw_content\": null}, {\"title\": \"Bill Cosby is Here to See You - South Park Studios US\", \"url\": \"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\", \"content\": \"01:56 It's Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde's performance and calls the Record Producer to try and fix it. 01:24 Lorde's Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac's hologram. 01:37 I've Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\", \"score\": 0.6643884, \"raw_content\": null}, {\"title\": \"Bill Cosby (android) | South Park Character ... - South Park Studios US\", \"url\": \"https://southpark.cc.com/wiki/Bill_Cosby_(android)\", \"content\": \"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman's Dawson's Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\"Bill Cosby\\\" is really VSM471, an android or cyborg of some kind engineered by 'hoomans' in the distant future. He fails in his initial missions to infiltrate South Park Elementary's 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski's aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\", \"score\": 0.5052006, \"raw_content\": null}, {\"title\": \"Trapper Keeper (South Park) - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\", \"content\": \"\\\"Trapper Keeper\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman's new Trapper Keeper, while Mr. Garrison's kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election's outcome.[2] \\\"Trapper Keeper\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\"Trapper Keeper\\\" Full episode at South Park Studios\", \"score\": 0.3839421, \"raw_content\": null}, {\"title\": \"Bill Cosby | South Park Archives | Fandom\", \"url\": \"https://southpark.fandom.com/wiki/Bill_Cosby\", \"content\": \"SIGN IN CHARACTERS SIGN IN Explore EXPLORE CHARACTERS SIGN IN TO EDIT Character Information For other uses, see Bill (Disambiguation). Bill Cosby is elderly, having gray hair as well as various facial wrinkles. More Information: Criminal Celebrities More Information: Movie Celebrities Minor Characters from Season Four More information: List of Minor Characters from Season Four | Season Four Community content is available under CC-BY-SA unless otherwise noted. EXPLORE PROPERTIES FOLLOW US Terms of Use Global Sitemap Local Sitemap Follow on IG\", \"score\": 0.34707275, \"raw_content\": null}]}\u001b[0m\n", + "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Bill Cosby South Park episode\", \"top_k\": [{\"title\": \"Bill Cosby and Taylor Swift Duet - South Park Studios\", \"url\": \"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\", \"content\": \"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift's rendition of \\\"It's Snowing Out There\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman's plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\", \"score\": 0.685971, \"raw_content\": null}, {\"title\": \"Bill Cosby is Here to See You - South Park Studios US\", \"url\": \"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\", \"content\": \"01:56 It's Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde's performance and calls the Record Producer to try and fix it. 01:24 Lorde's Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac's hologram. 01:37 I've Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\", \"score\": 0.6643884, \"raw_content\": null}, {\"title\": \"Bill Cosby (android) | South Park Character ... - South Park Studios US\", \"url\": \"https://southpark.cc.com/wiki/Bill_Cosby_(android)\", \"content\": \"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman's Dawson's Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\"Bill Cosby\\\" is really VSM471, an android or cyborg of some kind engineered by 'hoomans' in the distant future. He fails in his initial missions to infiltrate South Park Elementary's 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski's aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\", \"score\": 0.5052006, \"raw_content\": null}, {\"title\": \"\\\"South Park\\\" Clubhouses (TV Episode 1998) - IMDb\", \"url\": \"https://www.imdb.com/title/tt0705915/characters/nm0005295\", \"content\": \"\\\"South Park\\\" Clubhouses (TV Episode 1998) - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\", \"score\": 0.4604593, \"raw_content\": null}, {\"title\": \"Trapper Keeper (South Park) - Wikipedia\", \"url\": \"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\", \"content\": \"\\\"Trapper Keeper\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman's new Trapper Keeper, while Mr. Garrison's kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election's outcome.[2] \\\"Trapper Keeper\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\"Trapper Keeper\\\" Full episode at South Park Studios\", \"score\": 0.3839421, \"raw_content\": null}]}\u001b[0m\n", "\u001b[33minference> \u001b[0m\u001b[33mBill\u001b[0m\u001b[33m Cosby\u001b[0m\u001b[33m (\u001b[0m\u001b[33mBS\u001b[0m\u001b[33mM\u001b[0m\u001b[33m-\u001b[0m\u001b[33m471\u001b[0m\u001b[33m)\u001b[0m\u001b[33m first\u001b[0m\u001b[33m appears\u001b[0m\u001b[33m in\u001b[0m\u001b[33m the\u001b[0m\u001b[33m episode\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mTr\u001b[0m\u001b[33mapper\u001b[0m\u001b[33m Keeper\u001b[0m\u001b[33m\"\u001b[0m\u001b[33m (\u001b[0m\u001b[33mSeason\u001b[0m\u001b[33m \u001b[0m\u001b[33m4\u001b[0m\u001b[33m,\u001b[0m\u001b[33m Episode\u001b[0m\u001b[33m \u001b[0m\u001b[33m12\u001b[0m\u001b[33m)\u001b[0m\u001b[33m of\u001b[0m\u001b[33m South\u001b[0m\u001b[33m Park\u001b[0m\u001b[33m.\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[30m\u001b[0m\u001b[30m\u001b[0m\u001b[33minference> \u001b[0m\u001b[36m\u001b[0m\u001b[36mbr\u001b[0m\u001b[36mave\u001b[0m\u001b[36m_search\u001b[0m\u001b[36m.call\u001b[0m\u001b[36m(query\u001b[0m\u001b[36m=\"\u001b[0m\u001b[36mAndrew\u001b[0m\u001b[36m Tate\u001b[0m\u001b[36m kick\u001b[0m\u001b[36mboxing\u001b[0m\u001b[36m name\u001b[0m\u001b[36m\")\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[32mtool_execution> Tool:brave_search Args:{'query': 'Andrew Tate kickboxing name'}\u001b[0m\n", - "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Andrew Tate kickboxing name\", \"top_k\": [{\"title\": \"50 Facts About Andrew Tate - Facts.net\", \"url\": \"https://facts.net/andrew-tate-facts/\", \"content\": \"Full Name: Andrew Tate's full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\", \"score\": 0.8967681, \"raw_content\": null}, {\"title\": \"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\", \"url\": \"https://biographywallah.com/andrew-tate-biography/\", \"content\": \"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old (as of 2024)NationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\", \"score\": 0.80698997, \"raw_content\": null}, {\"title\": \"The Life Of Andrew Tate (By Andrew Tate Himself ... - Sidekick Boxing\", \"url\": \"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\", \"content\": \"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\u2019s life has been full of adventure.\", \"score\": 0.7817479, \"raw_content\": null}, {\"title\": \"50 Facts About Andrew Tate\", \"url\": \"https://facts.net/celebrity/50-facts-about-andrew-tate/\", \"content\": \"50 Facts About Andrew Tate - Facts.net Everything Else Facts Everything Else Facts 50 Facts About Andrew Tate Known for his kickboxing prowess, internet fame, and polarizing views, Tate's life is a blend of high achievements and significant legal troubles. Andrew Tate, a kickboxing champion turned internet personality, faced controversy and legal issues, showcasing the complexities of fame and the impact of social media influence on personal reputation. Andrew Tate's kickboxing career is one of his most notable achievements. Andrew Tate, a former professional kickboxer turned internet personality, has made waves online with his controversial opinions and business ventures. 20 Tristan Tate Facts A Deep Dive into the Life of a Controversial Figure 47 Facts About Larenz Tate More Facts\", \"score\": 0.61834323, \"raw_content\": null}, {\"title\": \"Andrew Tate Kickboxing Record: Legacy of King Cobra\", \"url\": \"https://stagbite.com/andrew-tate-kickboxing-record/\", \"content\": \"Andrew Tate Kickboxing Record: Legacy Of King Cobra \\u2013 Stagbite Andrew Tate Kickboxing Record: Legacy of King Cobra Andrew Tate Kickboxing Record: Legacy of King Cobra Over the course of his career, Andrew Tate amassed an impressive kickboxing record of 76 wins and 9 losses, with 23 of those victories coming via knockout or technical knockout. Andrew Tate\\u2019s Kickboxing Record What is Andrew Tate\\u2019s kickboxing record? Andrew Tate has a kickboxing record of 76 wins and 9 losses, with 23 wins coming via knockout or technical knockout. What titles did Andrew Tate win during his kickboxing career? We talk, write, and share some of the best Internet stories on Entertainment, Culture, Travel, Food, Books along with the social media trends & viral bees.\", \"score\": 0.59796065, \"raw_content\": null}]}\u001b[0m\n", + "\u001b[32mtool_execution> Tool:brave_search Response:{\"query\": \"Andrew Tate kickboxing name\", \"top_k\": [{\"title\": \"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\", \"url\": \"https://biographywallah.com/andrew-tate-biography/\", \"content\": \"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old (as of 2024)NationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\", \"score\": 0.80698997, \"raw_content\": null}, {\"title\": \"The Life Of Andrew Tate (By Andrew Tate Himself ... - Sidekick Boxing\", \"url\": \"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\", \"content\": \"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\u2019s life has been full of adventure.\", \"score\": 0.78194773, \"raw_content\": null}, {\"title\": \"Andrew Tate (\\\"King Cobra\\\") | MMA Fighter Page - Tapology\", \"url\": \"https://www.tapology.com/fightcenter/fighters/72139-andrew-tate\", \"content\": \"Andrew Tate (\\\"King Cobra\\\") | MMA Fighter Page | Tapology Andrew \\\"King Cobra\\\" Tate Andrew Tate Name: Andrew Tate Height: 6'1\\\" (185cm) | Reach: Andrew Tate is ineligible for Tapology's regional MMA rankings due to inactivity. Fighters must have at least one completed MMA bout in the past two years to be ranked. Andrew Tate MMA Fight Record Former top-ranked UFC fighter has called out Andrew Tate for having a paper title when it comes to combat... Andrew Tate \\u2022 All the biggest upcoming MMA & Boxing fights | UFC Fight Night | 02.01.2025, 12:00 PM ET | MMA Junkie: UFC Fight Night 249 video: Nine stoppages to open the year?! MMA Mania: Prochazka Vs. Hill: Odds, Full Fight Preview & Prediction\", \"score\": 0.6999322, \"raw_content\": null}, {\"title\": \"About Andrew Tate: A Journey from Champion to Controversy\", \"url\": \"https://reachmorpheus.com/andrew-tate/\", \"content\": \"Andrew Tate's kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\", \"score\": 0.6490677, \"raw_content\": null}, {\"title\": \"Andrew Tate's Kickboxing Career & Biography - MMA Full Contact\", \"url\": \"https://www.mmafullcontact.com/andrew-tate-kickboxing/\", \"content\": \"Andrew Tate's Kickboxing Career & Biography - MMA Full Contact Andrew Tate\\u2019s Kickboxing Career & Biography 2 Notable Opponents and Fights in Andrew Tate\\u2019s Kickboxing Career 4 Will Andrew Tate fight KSI? Notable Opponents and Fights in Andrew Tate\\u2019s Kickboxing Career Will Andrew Tate fight KSI? Similarly, Andrew Tate, known for his successful kickboxing career, has also shown interest in a potential fight with KSI. In conclusion, while there\\u2019s been plenty of interest and discussion about a potential boxing match between KSI and Andrew Tate, no official confirmation has been made as of now. With KSI\\u2019s upcoming match and Tate\\u2019s current personal circumstances, fans and followers of both personalities will have to wait for more updates on this potential fight.\", \"score\": 0.53050464, \"raw_content\": null}]}\u001b[0m\n", "\u001b[33minference> \u001b[0m\u001b[33mAndrew\u001b[0m\u001b[33m Tate\u001b[0m\u001b[33m's\u001b[0m\u001b[33m kick\u001b[0m\u001b[33mboxing\u001b[0m\u001b[33m name\u001b[0m\u001b[33m is\u001b[0m\u001b[33m \"\u001b[0m\u001b[33mKing\u001b[0m\u001b[33m Cobra\u001b[0m\u001b[33m.\"\u001b[0m\u001b[97m\u001b[0m\n", "\u001b[30m\u001b[0m" ] } ], "source": [ - "\n", + "# NBVAL_SKIP\n", "from llama_stack_client.lib.agents.agent import Agent\n", "from llama_stack_client.lib.agents.event_logger import EventLogger\n", "from llama_stack_client.types.agent_create_params import AgentConfig\n", @@ -3317,7 +3320,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "agkWgToGAsuA", "metadata": { "colab": { @@ -3332,7 +3335,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Getting traces for session_id=4c99812c-d3db-4555-a897-b592bf22b3e6\n" + "Getting traces for session_id=72993b3e-6030-44f5-9f48-664449d2b6d3\n" ] }, { @@ -3344,18 +3347,18 @@ "│ │ │ '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}'\n", "│ │ ],\n", - "│ │ 'output': \"content: tool_calls: [ToolCall(call_id='838a3846-0bc4-488e-9e42-65a48e29b80a', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\"\n", + "│ │ 'output': \"content: tool_calls: [ToolCall(call_id='8b7294ec-a83f-4798-ad8f-6bed662f08b6', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\"\n", "},\n", "{\n", - "│ │ 'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", - "│ │ 'output': '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}'\n", + "│ │ 'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", + "│ │ 'output': '{\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}'\n", "},\n", "{\n", "│ │ 'input': [\n", "│ │ │ '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}'\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}'\n", "│ │ ],\n", "│ │ 'output': 'content: The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves. tool_calls: []'\n", "},\n", @@ -3363,65 +3366,65 @@ "│ │ 'input': [\n", "│ │ │ '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}',\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}',\n", "│ │ │ '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}'\n", "│ │ ],\n", - "│ │ 'output': \"content: tool_calls: [ToolCall(call_id='ebd7e906-3ec9-45de-a58e-6662d75eceb7', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Bill Cosby South Park episode'})]\"\n", + "│ │ 'output': \"content: tool_calls: [ToolCall(call_id='fc0441bf-05ad-48d0-8034-4e19cb835904', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Bill Cosby South Park episode'})]\"\n", "},\n", "{\n", - "│ │ 'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n", - "│ │ 'output': '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'\n", + "│ │ 'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n", + "│ │ 'output': '{\"role\":\"tool\",\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses (TV Episode 1998) - IMDb\\\\\", \\\\\"url\\\\\": \\\\\"https://www.imdb.com/title/tt0705915/characters/nm0005295\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses (TV Episode 1998) - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\\\\\", \\\\\"score\\\\\": 0.4604593, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'\n", "},\n", "{\n", "│ │ 'input': [\n", "│ │ │ '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}',\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}',\n", "│ │ │ '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses (TV Episode 1998) - IMDb\\\\\", \\\\\"url\\\\\": \\\\\"https://www.imdb.com/title/tt0705915/characters/nm0005295\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses (TV Episode 1998) - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\\\\\", \\\\\"score\\\\\": 0.4604593, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'\n", "│ │ ],\n", - "│ │ 'output': 'content: Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \"Trapper Keeper\". tool_calls: []'\n", + "│ │ 'output': 'content: Bill Cosby (BSM-471) first appears in the episode \"Trapper Keeper\" (Season 4, Episode 12) of South Park. tool_calls: []'\n", "},\n", "{\n", "│ │ 'input': [\n", "│ │ │ '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}',\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}',\n", "│ │ │ '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses (TV Episode 1998) - IMDb\\\\\", \\\\\"url\\\\\": \\\\\"https://www.imdb.com/title/tt0705915/characters/nm0005295\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses (TV Episode 1998) - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\\\\\", \\\\\"score\\\\\": 0.4604593, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}',\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in the episode \\\\\"Trapper Keeper\\\\\" (Season 4, Episode 12) of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}'\n", "│ │ ],\n", - "│ │ 'output': \"content: tool_calls: [ToolCall(call_id='e26ecfb2-434c-479f-95dc-7b3b4929665a', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\"\n", + "│ │ 'output': \"content: tool_calls: [ToolCall(call_id='79276f65-3600-489d-ab41-d5a71dcaf075', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\"\n", "},\n", "{\n", - "│ │ 'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n", - "│ │ 'output': '{\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}]}\"}'\n", + "│ │ 'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"79276f65-3600-489d-ab41-d5a71dcaf075\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n", + "│ │ 'output': '{\"role\":\"tool\",\"call_id\":\"79276f65-3600-489d-ab41-d5a71dcaf075\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\\\\\", \\\\\"url\\\\\": \\\\\"https://biographywallah.com/andrew-tate-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\\\\\\\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old (as of 2024)NationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\\\\\\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\\\\\", \\\\\"score\\\\\": 0.80698997, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself ... - Sidekick Boxing\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\\\\\\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\\\\\\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\\\\\\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\\\\\\\u2019s life has been full of adventure.\\\\\", \\\\\"score\\\\\": 0.78194773, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate (\\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\") | MMA Fighter Page - Tapology\\\\\", \\\\\"url\\\\\": \\\\\"https://www.tapology.com/fightcenter/fighters/72139-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate (\\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\") | MMA Fighter Page | Tapology Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate Andrew Tate Name: Andrew Tate Height: 6\\'1\\\\\\\\\\\\\" (185cm) | Reach: Andrew Tate is ineligible for Tapology\\'s regional MMA rankings due to inactivity. Fighters must have at least one completed MMA bout in the past two years to be ranked. Andrew Tate MMA Fight Record Former top-ranked UFC fighter has called out Andrew Tate for having a paper title when it comes to combat... Andrew Tate \\\\\\\\u2022 All the biggest upcoming MMA & Boxing fights | UFC Fight Night | 02.01.2025, 12:00 PM ET | MMA Junkie: UFC Fight Night 249 video: Nine stoppages to open the year?! MMA Mania: Prochazka Vs. Hill: Odds, Full Fight Preview & Prediction\\\\\", \\\\\"score\\\\\": 0.6999322, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate\\'s Kickboxing Career & Biography - MMA Full Contact\\\\\", \\\\\"url\\\\\": \\\\\"https://www.mmafullcontact.com/andrew-tate-kickboxing/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing Career & Biography - MMA Full Contact Andrew Tate\\\\\\\\u2019s Kickboxing Career & Biography 2 Notable Opponents and Fights in Andrew Tate\\\\\\\\u2019s Kickboxing Career 4 Will Andrew Tate fight KSI? Notable Opponents and Fights in Andrew Tate\\\\\\\\u2019s Kickboxing Career Will Andrew Tate fight KSI? Similarly, Andrew Tate, known for his successful kickboxing career, has also shown interest in a potential fight with KSI. In conclusion, while there\\\\\\\\u2019s been plenty of interest and discussion about a potential boxing match between KSI and Andrew Tate, no official confirmation has been made as of now. With KSI\\\\\\\\u2019s upcoming match and Tate\\\\\\\\u2019s current personal circumstances, fans and followers of both personalities will have to wait for more updates on this potential fight.\\\\\", \\\\\"score\\\\\": 0.53050464, \\\\\"raw_content\\\\\": null}]}\"}'\n", "},\n", "{\n", "│ │ 'input': [\n", "│ │ │ '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}',\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null}]}\"}',\n", "│ │ │ '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses (TV Episode 1998) - IMDb\\\\\", \\\\\"url\\\\\": \\\\\"https://www.imdb.com/title/tt0705915/characters/nm0005295\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses (TV Episode 1998) - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\\\\\", \\\\\"score\\\\\": 0.4604593, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}',\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in the episode \\\\\"Trapper Keeper\\\\\" (Season 4, Episode 12) of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n", "│ │ │ '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}',\n", - "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n", - "│ │ │ '{\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}]}\"}'\n", + "│ │ │ '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"79276f65-3600-489d-ab41-d5a71dcaf075\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n", + "│ │ │ '{\"role\":\"tool\",\"call_id\":\"79276f65-3600-489d-ab41-d5a71dcaf075\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\\\\\", \\\\\"url\\\\\": \\\\\"https://biographywallah.com/andrew-tate-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\\\\\\\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old (as of 2024)NationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\\\\\\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\\\\\", \\\\\"score\\\\\": 0.80698997, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself ... - Sidekick Boxing\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\\\\\\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\\\\\\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\\\\\\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\\\\\\\u2019s life has been full of adventure.\\\\\", \\\\\"score\\\\\": 0.78194773, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate (\\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\") | MMA Fighter Page - Tapology\\\\\", \\\\\"url\\\\\": \\\\\"https://www.tapology.com/fightcenter/fighters/72139-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate (\\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\") | MMA Fighter Page | Tapology Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate Andrew Tate Name: Andrew Tate Height: 6\\'1\\\\\\\\\\\\\" (185cm) | Reach: Andrew Tate is ineligible for Tapology\\'s regional MMA rankings due to inactivity. Fighters must have at least one completed MMA bout in the past two years to be ranked. Andrew Tate MMA Fight Record Former top-ranked UFC fighter has called out Andrew Tate for having a paper title when it comes to combat... Andrew Tate \\\\\\\\u2022 All the biggest upcoming MMA & Boxing fights | UFC Fight Night | 02.01.2025, 12:00 PM ET | MMA Junkie: UFC Fight Night 249 video: Nine stoppages to open the year?! MMA Mania: Prochazka Vs. Hill: Odds, Full Fight Preview & Prediction\\\\\", \\\\\"score\\\\\": 0.6999322, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate\\'s Kickboxing Career & Biography - MMA Full Contact\\\\\", \\\\\"url\\\\\": \\\\\"https://www.mmafullcontact.com/andrew-tate-kickboxing/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing Career & Biography - MMA Full Contact Andrew Tate\\\\\\\\u2019s Kickboxing Career & Biography 2 Notable Opponents and Fights in Andrew Tate\\\\\\\\u2019s Kickboxing Career 4 Will Andrew Tate fight KSI? Notable Opponents and Fights in Andrew Tate\\\\\\\\u2019s Kickboxing Career Will Andrew Tate fight KSI? Similarly, Andrew Tate, known for his successful kickboxing career, has also shown interest in a potential fight with KSI. In conclusion, while there\\\\\\\\u2019s been plenty of interest and discussion about a potential boxing match between KSI and Andrew Tate, no official confirmation has been made as of now. With KSI\\\\\\\\u2019s upcoming match and Tate\\\\\\\\u2019s current personal circumstances, fans and followers of both personalities will have to wait for more updates on this potential fight.\\\\\", \\\\\"score\\\\\": 0.53050464, \\\\\"raw_content\\\\\": null}]}\"}'\n", "│ │ ],\n", - "│ │ 'output': 'content: Andrew Tate\\'s kickboxing name is \"Cobra Tate\" or \"King Cobra\". tool_calls: []'\n", + "│ │ 'output': 'content: Andrew Tate\\'s kickboxing name is \"King Cobra.\" tool_calls: []'\n", "}\n", "]\n", "\n" @@ -3433,18 +3436,18 @@ "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='838a3846-0bc4-488e-9e42-65a48e29b80a', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='8b7294ec-a83f-4798-ad8f-6bed662f08b6', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", @@ -3452,65 +3455,65 @@ "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='ebd7e906-3ec9-45de-a58e-6662d75eceb7', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='fc0441bf-05ad-48d0-8034-4e19cb835904', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses \u001b[0m\u001b[32m(\u001b[0m\u001b[32mTV Episode 1998\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - IMDb\\\\\", \\\\\"url\\\\\": \\\\\"https://www.imdb.com/title/tt0705915/characters/nm0005295\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses \u001b[0m\u001b[32m(\u001b[0m\u001b[32mTV Episode 1998\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\\\\\", \\\\\"score\\\\\": 0.4604593, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses \u001b[0m\u001b[32m(\u001b[0m\u001b[32mTV Episode 1998\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - IMDb\\\\\", \\\\\"url\\\\\": \\\\\"https://www.imdb.com/title/tt0705915/characters/nm0005295\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses \u001b[0m\u001b[32m(\u001b[0m\u001b[32mTV Episode 1998\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\\\\\", \\\\\"score\\\\\": 0.4604593, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in Season 4, Episode 12 of South Park, titled \"Trapper Keeper\". tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'content: Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the episode \"Trapper Keeper\" \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSeason 4, Episode 12\u001b[0m\u001b[32m)\u001b[0m\u001b[32m of South Park. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1;39m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m\u001b[39m: \u001b[0m\u001b[1;39m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses \u001b[0m\u001b[32m(\u001b[0m\u001b[32mTV Episode 1998\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - IMDb\\\\\", \\\\\"url\\\\\": \\\\\"https://www.imdb.com/title/tt0705915/characters/nm0005295\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses \u001b[0m\u001b[32m(\u001b[0m\u001b[32mTV Episode 1998\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\\\\\", \\\\\"score\\\\\": 0.4604593, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the episode \\\\\"Trapper Keeper\\\\\" \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSeason 4, Episode 12\u001b[0m\u001b[32m)\u001b[0m\u001b[32m of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1;39m]\u001b[0m\u001b[39m,\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='e26ecfb2-434c-479f-95dc-7b3b4929665a', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='79276f65-3600-489d-ab41-d5a71dcaf075', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"79276f65-3600-489d-ab41-d5a71dcaf075\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"79276f65-3600-489d-ab41-d5a71dcaf075\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\\\\\", \\\\\"url\\\\\": \\\\\"https://biographywallah.com/andrew-tate-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\\\\\\\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old \u001b[0m\u001b[32m(\u001b[0m\u001b[32mas of 2024\u001b[0m\u001b[32m)\u001b[0m\u001b[32mNationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\\\\\\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\\\\\", \\\\\"score\\\\\": 0.80698997, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself ... - Sidekick Boxing\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\\\\\\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\\\\\\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\\\\\\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\\\\\\\u2019s life has been full of adventure.\\\\\", \\\\\"score\\\\\": 0.78194773, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32m\\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\"\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | MMA Fighter Page - Tapology\\\\\", \\\\\"url\\\\\": \\\\\"https://www.tapology.com/fightcenter/fighters/72139-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32m\\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\"\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | MMA Fighter Page | Tapology Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate Andrew Tate Name: Andrew Tate Height: 6\\'1\\\\\\\\\\\\\" \u001b[0m\u001b[32m(\u001b[0m\u001b[32m185cm\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | Reach: Andrew Tate is ineligible for Tapology\\'s regional MMA rankings due to inactivity. Fighters must have at least one completed MMA bout in the past two years to be ranked. Andrew Tate MMA Fight Record Former top-ranked UFC fighter has called out Andrew Tate for having a paper title when it comes to combat... Andrew Tate \\\\\\\\u2022 All the biggest upcoming MMA & Boxing fights | UFC Fight Night | 02.01.2025, 12:00 PM ET | MMA Junkie: UFC Fight Night 249 video: Nine stoppages to open the year?! MMA Mania: Prochazka Vs. Hill: Odds, Full Fight Preview & Prediction\\\\\", \\\\\"score\\\\\": 0.6999322, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate\\'s Kickboxing Career & Biography - MMA Full Contact\\\\\", \\\\\"url\\\\\": \\\\\"https://www.mmafullcontact.com/andrew-tate-kickboxing/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing Career & Biography - MMA Full Contact Andrew Tate\\\\\\\\u2019s Kickboxing Career & Biography 2 Notable Opponents and Fights in Andrew Tate\\\\\\\\u2019s Kickboxing Career 4 Will Andrew Tate fight KSI? Notable Opponents and Fights in Andrew Tate\\\\\\\\u2019s Kickboxing Career Will Andrew Tate fight KSI? Similarly, Andrew Tate, known for his successful kickboxing career, has also shown interest in a potential fight with KSI. In conclusion, while there\\\\\\\\u2019s been plenty of interest and discussion about a potential boxing match between KSI and Andrew Tate, no official confirmation has been made as of now. With KSI\\\\\\\\u2019s upcoming match and Tate\\\\\\\\u2019s current personal circumstances, fans and followers of both personalities will have to wait for more updates on this potential fight.\\\\\", \\\\\"score\\\\\": 0.53050464, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round \u001b[0m\u001b[32m(\u001b[0m\u001b[32m1\u001b[0m\u001b[32m)\u001b[0m\u001b[32m Oklahoma City Thunder def. \u001b[0m\u001b[32m(\u001b[0m\u001b[32m8\u001b[0m\u001b[32m)\u001b[0m\u001b[32m New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"NBA Western Conference Finals 2024 teams\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"8b7294ec-a83f-4798-ad8f-6bed662f08b6\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown \u001b[0m\u001b[32m(\u001b[0m\u001b[32m20.8 / 5.4 / 5.0\u001b[0m\u001b[32m)\u001b[0m\u001b[32m 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m635\u001b[0m\u001b[32m)\u001b[0m\u001b[32m TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m208\u001b[0m\u001b[32m)\u001b[0m\u001b[32m AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 \u001b[0m\u001b[32m(\u001b[0m\u001b[32m178\u001b[0m\u001b[32m)\u001b[0m\u001b[32m WS: Derrick White \u001b[0m\u001b[32m(\u001b[0m\u001b[32m2.9\u001b[0m\u001b[32m)\u001b[0m\u001b[32m More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves \u001b[0m\u001b[32m(\u001b[0m\u001b[32m3\u001b[0m\u001b[32m)\u001b[0m\u001b[32m vs. Mavericks \u001b[0m\u001b[32m(\u001b[0m\u001b[32m5\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\\\\\", \\\\\"content\\\\\": \\\\\"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\\\\\", \\\\\"score\\\\\": 0.81979275, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"fc0441bf-05ad-48d0-8034-4e19cb835904\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mandroid\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses \u001b[0m\u001b[32m(\u001b[0m\u001b[32mTV Episode 1998\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - IMDb\\\\\", \\\\\"url\\\\\": \\\\\"https://www.imdb.com/title/tt0705915/characters/nm0005295\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"South Park\\\\\\\\\\\\\" Clubhouses \u001b[0m\u001b[32m(\u001b[0m\u001b[32mTV Episode 1998\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Trey Parker as Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 - IMDb Awards & Events Trey Parker: Stan Marsh, Eric Cartman, Phillip, Randy Marsh, Fat Abbot, Mr. Garrison, Mr. Mackey, 3rd Fat Abbot character, Roy, Teenage Boy #1, Clyde, Bill Cosby, Teenage Boy #2 Mr. Garrison : Stan, are you paying attention? Stan : Yes, Mr. Garrison. Stan Marsh : Dare. Stan Marsh : What? Release Dates | Official Sites | Company Credits | Filming & Production | Technical Specs Photo & Video User Lists Related lists from IMDb users 2024 Watched TV Shows\\\\\", \\\\\"score\\\\\": 0.4604593, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Trapper Keeper \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_\u001b[0m\u001b[32m(\u001b[0m\u001b[32mSouth_Park\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m The main plot of the episode involving the Trapper Keeper was written before the election,\u001b[0m\u001b[32m[\u001b[0m\u001b[32m1\u001b[0m\u001b[32m]\u001b[0m\u001b[32m but the subplot is a parody of the controversy surrounding the election\\'s outcome.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m2\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.\u001b[0m\u001b[32m[\u001b[0m\u001b[32m3\u001b[0m\u001b[32m]\u001b[0m\u001b[32m \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appears in the episode \\\\\"Trapper Keeper\\\\\" \u001b[0m\u001b[32m(\u001b[0m\u001b[32mSeason 4, Episode 12\u001b[0m\u001b[32m)\u001b[0m\u001b[32m of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself\u001b[0m\u001b[32m)\u001b[0m\u001b[32m\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"79276f65-3600-489d-ab41-d5a71dcaf075\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", + "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"tool\",\"call_id\":\"79276f65-3600-489d-ab41-d5a71dcaf075\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\\\\\", \\\\\"url\\\\\": \\\\\"https://biographywallah.com/andrew-tate-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\\\\\\\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old \u001b[0m\u001b[32m(\u001b[0m\u001b[32mas of 2024\u001b[0m\u001b[32m)\u001b[0m\u001b[32mNationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\\\\\\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\\\\\", \\\\\"score\\\\\": 0.80698997, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBy Andrew Tate Himself ... - Sidekick Boxing\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\\\\\\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\\\\\\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\\\\\\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\\\\\\\u2019s life has been full of adventure.\\\\\", \\\\\"score\\\\\": 0.78194773, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32m\\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\"\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | MMA Fighter Page - Tapology\\\\\", \\\\\"url\\\\\": \\\\\"https://www.tapology.com/fightcenter/fighters/72139-andrew-tate\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate \u001b[0m\u001b[32m(\u001b[0m\u001b[32m\\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\"\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | MMA Fighter Page | Tapology Andrew \\\\\\\\\\\\\"King Cobra\\\\\\\\\\\\\" Tate Andrew Tate Name: Andrew Tate Height: 6\\'1\\\\\\\\\\\\\" \u001b[0m\u001b[32m(\u001b[0m\u001b[32m185cm\u001b[0m\u001b[32m)\u001b[0m\u001b[32m | Reach: Andrew Tate is ineligible for Tapology\\'s regional MMA rankings due to inactivity. Fighters must have at least one completed MMA bout in the past two years to be ranked. Andrew Tate MMA Fight Record Former top-ranked UFC fighter has called out Andrew Tate for having a paper title when it comes to combat... Andrew Tate \\\\\\\\u2022 All the biggest upcoming MMA & Boxing fights | UFC Fight Night | 02.01.2025, 12:00 PM ET | MMA Junkie: UFC Fight Night 249 video: Nine stoppages to open the year?! MMA Mania: Prochazka Vs. Hill: Odds, Full Fight Preview & Prediction\\\\\", \\\\\"score\\\\\": 0.6999322, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate\\'s Kickboxing Career & Biography - MMA Full Contact\\\\\", \\\\\"url\\\\\": \\\\\"https://www.mmafullcontact.com/andrew-tate-kickboxing/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing Career & Biography - MMA Full Contact Andrew Tate\\\\\\\\u2019s Kickboxing Career & Biography 2 Notable Opponents and Fights in Andrew Tate\\\\\\\\u2019s Kickboxing Career 4 Will Andrew Tate fight KSI? Notable Opponents and Fights in Andrew Tate\\\\\\\\u2019s Kickboxing Career Will Andrew Tate fight KSI? Similarly, Andrew Tate, known for his successful kickboxing career, has also shown interest in a potential fight with KSI. In conclusion, while there\\\\\\\\u2019s been plenty of interest and discussion about a potential boxing match between KSI and Andrew Tate, no official confirmation has been made as of now. With KSI\\\\\\\\u2019s upcoming match and Tate\\\\\\\\u2019s current personal circumstances, fans and followers of both personalities will have to wait for more updates on this potential fight.\\\\\", \\\\\"score\\\\\": 0.53050464, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'content: Andrew Tate\\'s kickboxing name is \"Cobra Tate\" or \"King Cobra\". tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", + "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'content: Andrew Tate\\'s kickboxing name is \"King Cobra.\" tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", "\u001b[1m]\u001b[0m\n" ] @@ -3520,6 +3523,7 @@ } ], "source": [ + "# NBVAL_SKIP \n", "print(f\"Getting traces for session_id={session_id}\")\n", "import json\n", "\n", @@ -3554,7 +3558,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "sy4Xaff_Avuu", "metadata": { "colab": { @@ -3569,15 +3573,13 @@ "name": "stdout", "output_type": "stream", "text": [ - "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}'], 'output': \"content: tool_calls: [ToolCall(call_id='838a3846-0bc4-488e-9e42-65a48e29b80a', tool_name=, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\"}\n", - "{'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', 'output': '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}'}\n", - "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}'], 'output': 'content: The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves. tool_calls: []'}\n", - "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}'], 'output': \"content: tool_calls: [ToolCall(call_id='ebd7e906-3ec9-45de-a58e-6662d75eceb7', tool_name=, arguments={'query': 'Bill Cosby South Park episode'})]\"}\n", - "{'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', 'output': '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'}\n", - "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}'], 'output': 'content: Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \"Trapper Keeper\". tool_calls: []'}\n", - "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}'], 'output': \"content: tool_calls: [ToolCall(call_id='e26ecfb2-434c-479f-95dc-7b3b4929665a', tool_name=, arguments={'query': 'Andrew Tate kickboxing name'})]\"}\n", - "{'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}', 'output': '{\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}]}\"}'}\n", - "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"NBA Western Conference Finals 2024 teams\"}}]}', '{\"role\":\"tool\",\"call_id\":\"838a3846-0bc4-488e-9e42-65a48e29b80a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"NBA Western Conference Finals 2024 teams\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference Finals - Basketball-Reference.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\\\\\", \\\\\"content\\\\\": \\\\\"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (635) TRB: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (208) AST: Luka Don\\\\\\\\u010di\\\\\\\\u0107 (178) WS: Derrick White (2.9) More playoffs info\\\\\", \\\\\"score\\\\\": 0.9310187, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\\\\\", \\\\\"content\\\\\": \\\\\"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\\\\\", \\\\\"score\\\\\": 0.8914433, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nba.com/playoffs/2024/west-final\\\\\", \\\\\"content\\\\\": \\\\\"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\\\\\", \\\\\"score\\\\\": 0.8884594, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"NBA Conference Finals Schedule: Full List of Games & Results\\\\\", \\\\\"url\\\\\": \\\\\"https://www.si.com/nba/nba-conference-finals-schedule-full-list-of-games-results\\\\\", \\\\\"content\\\\\": \\\\\"The 2024 NBA conference finals matchups are set. Here\\'s the schedule for all the games. ... Western Conference First Round (1) Oklahoma City Thunder def. (8) New Orleans Pelicans in 4 games\\\\\", \\\\\"score\\\\\": 0.85008353, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"2024 NBA Western Conference playoff bracket - Basketnews.com\\\\\", \\\\\"url\\\\\": \\\\\"https://basketnews.com/news-204687-2024-nba-western-conference-playoff-bracket.html\\\\\", \\\\\"content\\\\\": \\\\\"In the 2024 NBA Western Conference playoffs, the Oklahoma City Thunder clinched the No. 1 seed. Every team from the Western Conference played their final game of the regular season, and two playoff pairs have been confirmed. The Los Angeles Lakers beat the New Orleans Pelicans, 110-106, in the Play-In Tournament to secure the 7th seed to set up a first-round matchup with the Denver Nuggets. Meanwhile, the Sacramento Kings will host the Golden State Warriors in the second Western Conference NBA Play-In Tournament game. The winners secure the No. 8 seed in the NBA playoffs for its conference. EuroLeague Play-In: Baskonia-Virtus game schedule announced\\\\\", \\\\\"score\\\\\": 0.8479807, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"ebd7e906-3ec9-45de-a58e-6662d75eceb7\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"\\'South Park\\' takes on Cosby, police, 2014 | CNN\\\\\", \\\\\"url\\\\\": \\\\\"https://www.cnn.com/2014/12/11/showbiz/tv/south-park-cosby-redskins-police/index.html\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\u2018South Park\\\\\\\\u2019 takes on Cosby, police, 2014 | CNN Watch Listen Live TV Subscribe Follow CNN Entertainment CNN Headlines CNN Shorts CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN CNN \\\\\\\\u00a0\\\\\\\\u2014\\\\\\\\u00a0 \\\\\\\\u201cI think we take for granted how Trey Parker can jam a heap of current issues into a storyline thats a smart and funny #SouthPark episode,\\\\\\\\u201d wrote Brent Veale. \\\\\\\\u201cOh Lorde, CartmanBrah, dead celebrity holograms, murdering cops, this #SouthPark episode is certainly making #SaveTheLivingRoom happen,\\\\\\\\u201d added Brett Pender. CNN Headlines CNN10 CNN Max CNN TV Schedules CNN 5 Things CNN Underscored CNN Crossword About CNN CNN Profiles CNN Newsletters Work for CNN Follow CNN Entertainment\\\\\", \\\\\"score\\\\\": 0.45391592, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in Season 4, Episode 12 of South Park, titled \\\\\"Trapper Keeper\\\\\".\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}', '{\"role\":\"tool\",\"call_id\":\"e26ecfb2-434c-479f-95dc-7b3b4929665a\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself)\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate stats. Fight Name: Cobra Tate. Born: 1 December 1986. Weight: 90 KG. Weight Class: Cruiserweight. Height: 1.92m. Fight Record: Wins - 76, Losses - 9. ... Andrew Tate\\'s Kickboxing Career. Andrew Tate has always fought credible opponents right from the beginning of his kickboxing career. One of his first professional fights on\\\\\", \\\\\"score\\\\\": 0.8795718, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.8752871, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.7992077, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.6490677, \\\\\"raw_content\\\\\": null}]}\"}'], 'output': 'content: Andrew Tate\\'s kickboxing name is \"Cobra Tate\" or \"King Cobra\". tool_calls: []'}\n" + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}'], 'output': 'content: Let me check the latest sports news. tool_calls: []'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}'], 'output': \"content: tool_calls: [ToolCall(call_id='26345b28-7f75-401e-88e3-77933cb70a2e', tool_name=, arguments={'query': 'Bill Cosby South Park episode'})]\"}\n", + "{'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"26345b28-7f75-401e-88e3-77933cb70a2e\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', 'output': '{\"role\":\"tool\",\"call_id\":\"26345b28-7f75-401e-88e3-77933cb70a2e\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"SIGN IN CHARACTERS SIGN IN Explore EXPLORE CHARACTERS SIGN IN TO EDIT Character Information For other uses, see Bill (Disambiguation). Bill Cosby is elderly, having gray hair as well as various facial wrinkles. More Information: Criminal Celebrities More Information: Movie Celebrities Minor Characters from Season Four More information: List of Minor Characters from Season Four | Season Four Community content is available under CC-BY-SA unless otherwise noted. EXPLORE PROPERTIES FOLLOW US Terms of Use Global Sitemap Local Sitemap Follow on IG\\\\\", \\\\\"score\\\\\": 0.34707275, \\\\\"raw_content\\\\\": null}]}\"}'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"26345b28-7f75-401e-88e3-77933cb70a2e\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"26345b28-7f75-401e-88e3-77933cb70a2e\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"SIGN IN CHARACTERS SIGN IN Explore EXPLORE CHARACTERS SIGN IN TO EDIT Character Information For other uses, see Bill (Disambiguation). Bill Cosby is elderly, having gray hair as well as various facial wrinkles. More Information: Criminal Celebrities More Information: Movie Celebrities Minor Characters from Season Four More information: List of Minor Characters from Season Four | Season Four Community content is available under CC-BY-SA unless otherwise noted. EXPLORE PROPERTIES FOLLOW US Terms of Use Global Sitemap Local Sitemap Follow on IG\\\\\", \\\\\"score\\\\\": 0.34707275, \\\\\"raw_content\\\\\": null}]}\"}'], 'output': 'content: Bill Cosby (BSM-471) first appears in the episode \"Trapper Keeper\" (Season 4, Episode 12) of South Park. tool_calls: []'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"26345b28-7f75-401e-88e3-77933cb70a2e\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"26345b28-7f75-401e-88e3-77933cb70a2e\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"SIGN IN CHARACTERS SIGN IN Explore EXPLORE CHARACTERS SIGN IN TO EDIT Character Information For other uses, see Bill (Disambiguation). Bill Cosby is elderly, having gray hair as well as various facial wrinkles. More Information: Criminal Celebrities More Information: Movie Celebrities Minor Characters from Season Four More information: List of Minor Characters from Season Four | Season Four Community content is available under CC-BY-SA unless otherwise noted. EXPLORE PROPERTIES FOLLOW US Terms of Use Global Sitemap Local Sitemap Follow on IG\\\\\", \\\\\"score\\\\\": 0.34707275, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in the episode \\\\\"Trapper Keeper\\\\\" (Season 4, Episode 12) of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}'], 'output': \"content: tool_calls: [ToolCall(call_id='fd4cc3c6-49d0-42e4-b0af-877e72f8d6ba', tool_name=, arguments={'query': 'Andrew Tate kickboxing name'})]\"}\n", + "{'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"fd4cc3c6-49d0-42e4-b0af-877e72f8d6ba\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}', 'output': '{\"role\":\"tool\",\"call_id\":\"fd4cc3c6-49d0-42e4-b0af-877e72f8d6ba\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\\\\\", \\\\\"url\\\\\": \\\\\"https://biographywallah.com/andrew-tate-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\\\\\\\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old (as of 2024)NationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\\\\\\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\\\\\", \\\\\"score\\\\\": 0.80698997, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself ... - Sidekick Boxing\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\\\\\\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\\\\\\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\\\\\\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\\\\\\\u2019s life has been full of adventure.\\\\\", \\\\\"score\\\\\": 0.7817479, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/celebrity/50-facts-about-andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net Everything Else Facts Everything Else Facts 50 Facts About Andrew Tate Known for his kickboxing prowess, internet fame, and polarizing views, Tate\\'s life is a blend of high achievements and significant legal troubles. Andrew Tate, a kickboxing champion turned internet personality, faced controversy and legal issues, showcasing the complexities of fame and the impact of social media influence on personal reputation. Andrew Tate\\'s kickboxing career is one of his most notable achievements. Andrew Tate, a former professional kickboxer turned internet personality, has made waves online with his controversial opinions and business ventures. 20 Tristan Tate Facts A Deep Dive into the Life of a Controversial Figure 47 Facts About Larenz Tate More Facts\\\\\", \\\\\"score\\\\\": 0.61834323, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate Kickboxing Record: Legacy of King Cobra\\\\\", \\\\\"url\\\\\": \\\\\"https://stagbite.com/andrew-tate-kickboxing-record/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Kickboxing Record: Legacy Of King Cobra \\\\\\\\u2013 Stagbite Andrew Tate Kickboxing Record: Legacy of King Cobra Andrew Tate Kickboxing Record: Legacy of King Cobra Over the course of his career, Andrew Tate amassed an impressive kickboxing record of 76 wins and 9 losses, with 23 of those victories coming via knockout or technical knockout. Andrew Tate\\\\\\\\u2019s Kickboxing Record What is Andrew Tate\\\\\\\\u2019s kickboxing record? Andrew Tate has a kickboxing record of 76 wins and 9 losses, with 23 wins coming via knockout or technical knockout. What titles did Andrew Tate win during his kickboxing career? We talk, write, and share some of the best Internet stories on Entertainment, Culture, Travel, Food, Books along with the social media trends & viral bees.\\\\\", \\\\\"score\\\\\": 0.59796065, \\\\\"raw_content\\\\\": null}]}\"}'}\n", + "{'input': ['{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}', '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"26345b28-7f75-401e-88e3-77933cb70a2e\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}', '{\"role\":\"tool\",\"call_id\":\"26345b28-7f75-401e-88e3-77933cb70a2e\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Bill Cosby South Park episode\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Bill Cosby and Taylor Swift Duet - South Park Studios\\\\\", \\\\\"url\\\\\": \\\\\"https://www.southparkstudios.com/video-clips/90r7i1/south-park-bill-cosby-and-taylor-swift-duet\\\\\", \\\\\"content\\\\\": \\\\\"01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:03 Bill Cosby and Taylor Swift Duet South ParkS18 E10 ------------------------------------------------------- The holiday special continues with Bill Cosby and Taylor Swift\\'s rendition of \\\\\\\\\\\\\"It\\'s Snowing Out There\\\\\\\\\\\\\". 01:31 #WeBelieveInYou South ParkS18 E10 -------------------------------------- With everyone watching, Kyle takes the opportunity to reach out to his brother. 01:47 Watch Your Microaggressions, Bro South ParkS19 E1 ------------------------------------------------------ Cartman\\'s plan to frame PC Principal backfires. South ParkS19 E1 -------------------------------------- After hearing that the PC people have targeted Kyle, Cartman vows to help.\\\\\", \\\\\"score\\\\\": 0.685971, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby is Here to See You - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/video-clips/wfot8s/south-park-bill-cosby-is-here-to-see-you\\\\\", \\\\\"content\\\\\": \\\\\"01:56 It\\'s Not About Music South ParkS18 E9 ------------------------------------------ At home, Randy sees the consequences of Lorde\\'s performance and calls the Record Producer to try and fix it. 01:24 Lorde\\'s Hologram South ParkS18 E9 -------------------------------------- The Record Producer reveals the truth about the music industry... South ParkS18 E9 --------------------------------------------- Randy catches Sharon with Tupac\\'s hologram. 01:37 I\\'ve Got Your Son, Lorde South ParkS18 E10 ----------------------------------------------- The Record Producer takes Stan and Kyle hostage. 01:05 Bill Cosby is Here to See You South ParkS18 E10 ---------------------------------------------------- Bill Cosby recruits Kyle and his hashtag for the big Holiday Special. 01:21 Lorde Is My Dad South ParkS18 E10 -------------------------------------- After trying to confront Cartman Bra, Stan finally reveals the truth about his dad.\\\\\", \\\\\"score\\\\\": 0.6643884, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby (android) | South Park Character ... - South Park Studios US\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.cc.com/wiki/Bill_Cosby_(android)\\\\\", \\\\\"content\\\\\": \\\\\"Bill Cosby (android) | South Park Character / Location / User talk etc | Official South Park Studios Wiki Sent back in time to destroy Eric Cartman\\'s Dawson\\'s Creek Trapper Keeper before it manifests into an omnipotent supercomputer that can destroy all humanity, \\\\\\\\\\\\\"Bill Cosby\\\\\\\\\\\\\" is really VSM471, an android or cyborg of some kind engineered by \\'hoomans\\' in the distant future. He fails in his initial missions to infiltrate South Park Elementary\\'s 4th Grade class, destroy the Trapper Keeper or Cartman himself, but with Stan Marsh and Kyle Broflovski\\'s aid, he is able to succeed in preventing his dismal future, and painfully fades from existence. South Park and all related titles, logos and characters are trademarks of Comedy Partners.\\\\\", \\\\\"score\\\\\": 0.5052006, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Trapper Keeper (South Park) - Wikipedia\\\\\", \\\\\"url\\\\\": \\\\\"https://en.wikipedia.org/wiki/Trapper_Keeper_(South_Park)\\\\\", \\\\\"content\\\\\": \\\\\"\\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" is the twelfth episode of the fourth season of the animated television series South Park, and the 60th episode of the series overall. In the episode, a man from the future wants Cartman\\'s new Trapper Keeper, while Mr. Garrison\\'s kindergarten class holds an election for class president with confusing results. It is one of the many South Park episodes that parodies a current event.[1] The main plot of the episode involving the Trapper Keeper was written before the election,[1] but the subplot is a parody of the controversy surrounding the election\\'s outcome.[2] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" did not originally feature the election storyline, only a subplot about Ike attending his first day of kindergarten.[3] \\\\\\\\\\\\\"Trapper Keeper\\\\\\\\\\\\\" Full episode at South Park Studios\\\\\", \\\\\"score\\\\\": 0.3839421, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Bill Cosby | South Park Archives | Fandom\\\\\", \\\\\"url\\\\\": \\\\\"https://southpark.fandom.com/wiki/Bill_Cosby\\\\\", \\\\\"content\\\\\": \\\\\"SIGN IN CHARACTERS SIGN IN Explore EXPLORE CHARACTERS SIGN IN TO EDIT Character Information For other uses, see Bill (Disambiguation). Bill Cosby is elderly, having gray hair as well as various facial wrinkles. More Information: Criminal Celebrities More Information: Movie Celebrities Minor Characters from Season Four More information: List of Minor Characters from Season Four | Season Four Community content is available under CC-BY-SA unless otherwise noted. EXPLORE PROPERTIES FOLLOW US Terms of Use Global Sitemap Local Sitemap Follow on IG\\\\\", \\\\\"score\\\\\": 0.34707275, \\\\\"raw_content\\\\\": null}]}\"}', '{\"role\":\"assistant\",\"content\":\"Bill Cosby (BSM-471) first appears in the episode \\\\\"Trapper Keeper\\\\\" (Season 4, Episode 12) of South Park.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}', '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}', '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"fd4cc3c6-49d0-42e4-b0af-877e72f8d6ba\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}', '{\"role\":\"tool\",\"call_id\":\"fd4cc3c6-49d0-42e4-b0af-877e72f8d6ba\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/andrew-tate-facts/\\\\\", \\\\\"content\\\\\": \\\\\"Full Name: Andrew Tate\\'s full name is Emory Andrew Tate III, named after his father, a celebrated chess player. Date of Birth: ... Kickboxing Start: Tate began his kickboxing career in 2005, starting his journey as a professional fighter, which would later be a significant part of his persona. First Championship:\\\\\", \\\\\"score\\\\\": 0.8967681, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth\\\\\", \\\\\"url\\\\\": \\\\\"https://biographywallah.com/andrew-tate-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth \\\\\\\\u00bb Biography Wallah Andrew Tate Age, Height, Weight, Family, Parents, Biography, Net Worth Andrew Tate Biography NameAndrew TateReal nameEmory Andrew Tate IIIProfession \\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0\\\\\\\\u00a0Kickboxer, Commentator and BusinessmanDate of birth14 December 1986BirthplaceWashington D.C., United StatesAndrew Tate Age37 years old (as of 2024)NationalityBritish-AmericanZodiac SignSagittariusGenderMaleSchoolLocal School in Washington D.C., United StatesGirlfriend/SpouseNaghel GeorgianaSexual OrientationStraightNet worth$1000 Million Who is Andrew Tate? Andrew Tate is a British-American former professional kickboxing world champion businessman and media personality, who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate Age Andrew Tate was born on 1 December 1986 and is 37 years old. Andrew Tate\\\\\\\\u2019s Net Worth What is the net worth of Andrew Tate? Where is Andrew Tate from? How old is Andrew Tate?\\\\\", \\\\\"score\\\\\": 0.80698997, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"The Life Of Andrew Tate (By Andrew Tate Himself ... - Sidekick Boxing\\\\\", \\\\\"url\\\\\": \\\\\"https://sidekickboxing.co.uk/the-life-of-andrew-king-cobra-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate is a British-American former professional kickboxing world champion who fought in the cruiserweight and super cruiserweight divisions. Andrew Tate\\\\\\\\u2019s Kickboxing Career Andrew Tate in the Big Brother house Andrew Tate\\\\\\\\u2019s Kickboxing World Titles and his Sidekick boxing gloves Andrew Tate After Kickboxing Andrew Tate and his brother Tristan moved to Romania to set up their empire of businesses including trading in Bitcoin, Hustlers University, CobraTate.com, The Real World, and The War Room. From being a 4x kickboxing world champion to becoming the world\\\\\\\\u2019s most Googled man in the world with a private jet and over 33 cars, Andrew Tate\\\\\\\\u2019s life has been full of adventure.\\\\\", \\\\\"score\\\\\": 0.7817479, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"50 Facts About Andrew Tate\\\\\", \\\\\"url\\\\\": \\\\\"https://facts.net/celebrity/50-facts-about-andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"50 Facts About Andrew Tate - Facts.net Everything Else Facts Everything Else Facts 50 Facts About Andrew Tate Known for his kickboxing prowess, internet fame, and polarizing views, Tate\\'s life is a blend of high achievements and significant legal troubles. Andrew Tate, a kickboxing champion turned internet personality, faced controversy and legal issues, showcasing the complexities of fame and the impact of social media influence on personal reputation. Andrew Tate\\'s kickboxing career is one of his most notable achievements. Andrew Tate, a former professional kickboxer turned internet personality, has made waves online with his controversial opinions and business ventures. 20 Tristan Tate Facts A Deep Dive into the Life of a Controversial Figure 47 Facts About Larenz Tate More Facts\\\\\", \\\\\"score\\\\\": 0.61834323, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate Kickboxing Record: Legacy of King Cobra\\\\\", \\\\\"url\\\\\": \\\\\"https://stagbite.com/andrew-tate-kickboxing-record/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Kickboxing Record: Legacy Of King Cobra \\\\\\\\u2013 Stagbite Andrew Tate Kickboxing Record: Legacy of King Cobra Andrew Tate Kickboxing Record: Legacy of King Cobra Over the course of his career, Andrew Tate amassed an impressive kickboxing record of 76 wins and 9 losses, with 23 of those victories coming via knockout or technical knockout. Andrew Tate\\\\\\\\u2019s Kickboxing Record What is Andrew Tate\\\\\\\\u2019s kickboxing record? Andrew Tate has a kickboxing record of 76 wins and 9 losses, with 23 wins coming via knockout or technical knockout. What titles did Andrew Tate win during his kickboxing career? We talk, write, and share some of the best Internet stories on Entertainment, Culture, Travel, Food, Books along with the social media trends & viral bees.\\\\\", \\\\\"score\\\\\": 0.59796065, \\\\\"raw_content\\\\\": null}]}\"}'], 'output': 'content: Andrew Tate\\'s kickboxing name is \"King Cobra.\" tool_calls: []'}\n" ] }, { @@ -3586,17 +3588,17 @@ "
[\n",
               "{\n",
               "│   │   'input_query': '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='838a3846-0bc4-488e-9e42-65a48e29b80a', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'NBA Western Conference Finals 2024 teams'})]\",\n",
-              "│   │   'expected_answer': 'brave_search'\n",
-              "},\n",
-              "{\n",
-              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
-              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='ebd7e906-3ec9-45de-a58e-6662d75eceb7', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Bill Cosby South Park episode'})]\",\n",
+              "│   │   'generated_answer': 'content: Let me check the latest sports news. tool_calls: []',\n",
+              "│   │   'expected_answer': 'brave_search'\n",
+              "},\n",
+              "{\n",
+              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
+              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='26345b28-7f75-401e-88e3-77933cb70a2e', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Bill Cosby South Park episode'})]\",\n",
               "│   │   'expected_answer': 'brave_search'\n",
               "},\n",
               "{\n",
               "│   │   'input_query': '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}',\n",
-              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='e26ecfb2-434c-479f-95dc-7b3b4929665a', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\",\n",
+              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='fd4cc3c6-49d0-42e4-b0af-877e72f8d6ba', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\",\n",
               "│   │   'expected_answer': 'brave_search'\n",
               "}\n",
               "]\n",
@@ -3606,17 +3608,17 @@
               "\u001b[1m[\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1m{\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='838a3846-0bc4-488e-9e42-65a48e29b80a', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'NBA Western Conference Finals 2024 teams'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[1;39m{\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='ebd7e906-3ec9-45de-a58e-6662d75eceb7', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'content: Let me check the latest sports news. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m,\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'expected_answer'\u001b[0m: \u001b[32m'brave_search'\u001b[0m\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[1m{\u001b[0m\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='26345b28-7f75-401e-88e3-77933cb70a2e', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'>, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\u001b[39m,\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'expected_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'brave_search'\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1;39m}\u001b[0m\u001b[39m,\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1;39m{\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'input_query'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\u001b[39m,\u001b[0m\n",
-              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='e26ecfb2-434c-479f-95dc-7b3b4929665a', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m,\n",
+              "\u001b[2;32m│   │   \u001b[0m\u001b[32m'generated_answer'\u001b[0m\u001b[39m: \u001b[0m\u001b[32m\"content:  tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='fd4cc3c6-49d0-42e4-b0af-877e72f8d6ba', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m,\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'expected_answer'\u001b[0m: \u001b[32m'brave_search'\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
               "\u001b[1m]\u001b[0m\n"
@@ -3631,8 +3633,8 @@
               "
ScoringScoreResponse(\n",
               "results={\n",
               "│   │   'basic::subset_of': ScoringResult(\n",
-              "│   │   │   aggregated_results={'accuracy': {'accuracy': 1.0, 'num_correct': 3.0, 'num_total': 3}},\n",
-              "│   │   │   score_rows=[{'score': 1.0}, {'score': 1.0}, {'score': 1.0}]\n",
+              "│   │   │   aggregated_results={'accuracy': {'accuracy': 0.6666666666666666, 'num_correct': 2.0, 'num_total': 3}},\n",
+              "│   │   │   score_rows=[{'score': 0.0}, {'score': 1.0}, {'score': 1.0}]\n",
               "│   │   )\n",
               "}\n",
               ")\n",
@@ -3642,8 +3644,8 @@
               "\u001b[1;35mScoringScoreResponse\u001b[0m\u001b[1m(\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mresults\u001b[0m=\u001b[1m{\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[32m'basic::subset_of'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n",
-              "\u001b[2;32m│   │   │   \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m3.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m3\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n",
-              "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n",
+              "\u001b[2;32m│   │   │   \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m0.6666666666666666\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m2.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m3\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n",
+              "\u001b[2;32m│   │   │   \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n",
               "\u001b[2;32m│   │   \u001b[0m\u001b[1m)\u001b[0m\n",
               "\u001b[2;32m│   \u001b[0m\u001b[1m}\u001b[0m\n",
               "\u001b[1m)\u001b[0m\n"
@@ -3654,6 +3656,7 @@
         }
       ],
       "source": [
+        "# NBVAL_SKIP\n",
         "# post-process telemetry spance and prepare data for eval\n",
         "# in this case, we want to assert that all user prompts is followed by a tool call\n",
         "import ast\n",
@@ -3757,6 +3760,7 @@
         }
       ],
       "source": [
+        "# NBVAL_SKIP\n",
         "import rich\n",
         "from rich.pretty import pprint\n",
         "\n",

From 4dd4f09fc510e662a0b83c892f18cd51a9af3ad0 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 15:27:29 -0800
Subject: [PATCH 082/107] Rename a test and add some comments

---
 tests/client-sdk/agents/test_agents.py | 5 ++++-
 tests/client-sdk/metadata.py           | 2 +-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py
index 6fe0678b4..f1da4e124 100644
--- a/tests/client-sdk/agents/test_agents.py
+++ b/tests/client-sdk/agents/test_agents.py
@@ -212,7 +212,10 @@ def test_builtin_tool_code_execution(llama_stack_client, agent_config):
     assert "Tool:code_interpreter Response" in logs_str
 
 
-def test_code_execution(llama_stack_client, agent_config):
+# This test must be run in an environment where `bwrap` is available. If you are running against a
+# server, this means the _server_ must have `bwrap` available. If you are using library client, then
+# you must have `bwrap` available in test's environment.
+def test_code_interpreter_for_attachments(llama_stack_client, agent_config):
     agent_config = {
         **agent_config,
         "toolgroups": [
diff --git a/tests/client-sdk/metadata.py b/tests/client-sdk/metadata.py
index d8d6616c2..1a87c6bd0 100644
--- a/tests/client-sdk/metadata.py
+++ b/tests/client-sdk/metadata.py
@@ -38,7 +38,7 @@ AGENTS_API_TEST_MAP = {
     "create_agent_turn": {
         "rag": ["test_rag_agent"],
         "custom_tool": ["test_custom_tool"],
-        "code_execution": ["test_code_execution"],
+        "code_execution": ["test_code_interpreter_for_attachments"],
     }
 }
 

From f4f47970e536f007a53ba0dbe23026a5a1e13821 Mon Sep 17 00:00:00 2001
From: Sixian Yi 
Date: Wed, 22 Jan 2025 15:35:19 -0800
Subject: [PATCH 083/107] [client sdk test] add options for inference_model,
 safety_shield, embedding_model (#843)

# What does this PR do?
Default inference_model for testing: "meta-llama/Llama-3.1-8B-Instruct"
Default vision inference_model for testing:
"meta-llama/Llama-3.2-11B-Vision-Instruct"


## Test Plan
`/opt/miniconda3/envs/stack/bin/pytest -s -v
--inference-model=meta-llama/Llama-3.2-3B-Instruct
tests/client-sdk/agents`


`/opt/miniconda3/envs/stack/bin/pytest -s -v
--embedding-model=all-MiniLM-L6-v2 tests/client-sdk/vector_io`

`/opt/miniconda3/envs/stack/bin/pytest -s -v
--safety-shield=meta-llama/Llama-Guard-3-1B tests/client-sdk/safety`

## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 tests/client-sdk/agents/test_agents.py       | 12 ------
 tests/client-sdk/conftest.py                 | 35 +++++++++++++++--
 tests/client-sdk/inference/test_inference.py | 26 +------------
 tests/client-sdk/safety/conftest.py          | 22 +++++++++++
 tests/client-sdk/safety/test_safety.py       | 10 -----
 tests/client-sdk/vector_io/conftest.py       | 22 +++++++++++
 tests/client-sdk/vector_io/test_vector_io.py | 40 ++++----------------
 7 files changed, 84 insertions(+), 83 deletions(-)
 create mode 100644 tests/client-sdk/safety/conftest.py
 create mode 100644 tests/client-sdk/vector_io/conftest.py

diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py
index f1da4e124..7c13f5768 100644
--- a/tests/client-sdk/agents/test_agents.py
+++ b/tests/client-sdk/agents/test_agents.py
@@ -79,18 +79,6 @@ class TestClientTool(ClientTool):
             return -1
 
 
-@pytest.fixture(scope="session")
-def text_model_id(llama_stack_client):
-    available_models = [
-        model.identifier
-        for model in llama_stack_client.models.list()
-        if model.identifier.startswith("meta-llama") and "405" not in model.identifier
-    ]
-    model_id = available_models[0]
-    print(f"Using model: {model_id}")
-    return model_id
-
-
 @pytest.fixture(scope="session")
 def agent_config(llama_stack_client, text_model_id):
     available_shields = [
diff --git a/tests/client-sdk/conftest.py b/tests/client-sdk/conftest.py
index c19546887..0f0733010 100644
--- a/tests/client-sdk/conftest.py
+++ b/tests/client-sdk/conftest.py
@@ -20,6 +20,10 @@ def pytest_configure(config):
         config.pluginmanager.register(Report())
 
 
+TEXT_MODEL = "meta-llama/Llama-3.1-8B-Instruct"
+VISION_MODEL = "meta-llama/Llama-3.2-11B-Vision-Instruct"
+
+
 def pytest_addoption(parser):
     parser.addoption(
         "--report",
@@ -27,10 +31,18 @@ def pytest_addoption(parser):
         action="store_true",
         help="Knob to determine if we should generate report, e.g. --output=True",
     )
-
-
-TEXT_MODEL = "meta-llama/Llama-3.1-8B-Instruct"
-INFERENCE_MODEL = "meta-llama/Llama-3.2-11B-Vision-Instruct"
+    parser.addoption(
+        "--inference-model",
+        action="store",
+        default=TEXT_MODEL,
+        help="Specify the inference model to use for testing",
+    )
+    parser.addoption(
+        "--vision-inference-model",
+        action="store",
+        default=VISION_MODEL,
+        help="Specify the vision inference model to use for testing",
+    )
 
 
 @pytest.fixture(scope="session")
@@ -61,3 +73,18 @@ def llama_stack_client(provider_data):
     else:
         raise ValueError("LLAMA_STACK_CONFIG or LLAMA_STACK_BASE_URL must be set")
     return client
+
+
+def pytest_generate_tests(metafunc):
+    if "text_model_id" in metafunc.fixturenames:
+        metafunc.parametrize(
+            "text_model_id",
+            [metafunc.config.getoption("--inference-model")],
+            scope="session",
+        )
+    if "vision_model_id" in metafunc.fixturenames:
+        metafunc.parametrize(
+            "vision_model_id",
+            [metafunc.config.getoption("--vision-inference-model")],
+            scope="session",
+        )
diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py
index b1f1dd139..01bbd7dc0 100644
--- a/tests/client-sdk/inference/test_inference.py
+++ b/tests/client-sdk/inference/test_inference.py
@@ -34,30 +34,6 @@ def inference_provider_type(llama_stack_client):
     return inference_providers[0].provider_type
 
 
-@pytest.fixture(scope="session")
-def text_model_id(llama_stack_client):
-    available_models = [
-        model.identifier
-        for model in llama_stack_client.models.list()
-        if model.identifier.startswith("meta-llama") and "405" not in model.identifier
-    ]
-    assert len(available_models) > 0
-    return available_models[0]
-
-
-@pytest.fixture(scope="session")
-def vision_model_id(llama_stack_client):
-    available_models = [
-        model.identifier
-        for model in llama_stack_client.models.list()
-        if "vision" in model.identifier.lower()
-    ]
-    if len(available_models) == 0:
-        pytest.skip("No vision models available")
-
-    return available_models[0]
-
-
 @pytest.fixture
 def get_weather_tool_definition():
     return {
@@ -107,6 +83,7 @@ def test_text_completion_streaming(llama_stack_client, text_model_id):
     assert "blue" in "".join(streamed_content).lower().strip()
 
 
+@pytest.mark.skip("Most inference providers don't support log probs yet")
 def test_completion_log_probs_non_streaming(llama_stack_client, text_model_id):
     response = llama_stack_client.inference.completion(
         content="Complete the sentence: Micheael Jordan is born in ",
@@ -124,6 +101,7 @@ def test_completion_log_probs_non_streaming(llama_stack_client, text_model_id):
     assert all(len(logprob.logprobs_by_token) == 3 for logprob in response.logprobs)
 
 
+@pytest.mark.skip("Most inference providers don't support log probs yet")
 def test_completion_log_probs_streaming(llama_stack_client, text_model_id):
     response = llama_stack_client.inference.completion(
         content="Complete the sentence: Micheael Jordan is born in ",
diff --git a/tests/client-sdk/safety/conftest.py b/tests/client-sdk/safety/conftest.py
new file mode 100644
index 000000000..9c5ff7352
--- /dev/null
+++ b/tests/client-sdk/safety/conftest.py
@@ -0,0 +1,22 @@
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+# All rights reserved.
+#
+# This source code is licensed under the terms described in the LICENSE file in
+# the root directory of this source tree.
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--safety_shield",
+        action="store",
+        default="meta-llama/Llama-Guard-3-1B",
+        help="Specify the safety shield model to use for testing",
+    )
+
+
+def pytest_generate_tests(metafunc):
+    if "llama_guard_text_shield_id" in metafunc.fixturenames:
+        metafunc.parametrize(
+            "llama_guard_text_shield_id",
+            [metafunc.config.getoption("--safety_shield")],
+        )
diff --git a/tests/client-sdk/safety/test_safety.py b/tests/client-sdk/safety/test_safety.py
index ac3221364..7456fb88f 100644
--- a/tests/client-sdk/safety/test_safety.py
+++ b/tests/client-sdk/safety/test_safety.py
@@ -32,16 +32,6 @@ def available_shields(llama_stack_client):
     return [shield.identifier for shield in llama_stack_client.shields.list()]
 
 
-@pytest.fixture(scope="session")
-def llama_guard_text_shield_id(available_shields):
-    if "meta-llama/Llama-Guard-3-1B" in available_shields:
-        return "meta-llama/Llama-Guard-3-1B"
-    elif "meta-llama/Llama-Guard-3-8B" in available_shields:
-        return "meta-llama/Llama-Guard-3-8B"
-    else:
-        pytest.skip("Llama-Guard shield is not available. Skipping.")
-
-
 @pytest.fixture(scope="session")
 def code_scanner_shield_id(available_shields):
     if "CodeScanner" in available_shields:
diff --git a/tests/client-sdk/vector_io/conftest.py b/tests/client-sdk/vector_io/conftest.py
new file mode 100644
index 000000000..64cac27d2
--- /dev/null
+++ b/tests/client-sdk/vector_io/conftest.py
@@ -0,0 +1,22 @@
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+# All rights reserved.
+#
+# This source code is licensed under the terms described in the LICENSE file in
+# the root directory of this source tree.
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--embedding-model",
+        action="store",
+        default="all-MiniLM-L6-v2",
+        help="Specify the embedding model to use for testing",
+    )
+
+
+def pytest_generate_tests(metafunc):
+    if "embedding_model" in metafunc.fixturenames:
+        metafunc.parametrize(
+            "embedding_model",
+            [metafunc.config.getoption("--embedding-model")],
+        )
diff --git a/tests/client-sdk/vector_io/test_vector_io.py b/tests/client-sdk/vector_io/test_vector_io.py
index 04b639667..20e49d805 100644
--- a/tests/client-sdk/vector_io/test_vector_io.py
+++ b/tests/client-sdk/vector_io/test_vector_io.py
@@ -6,39 +6,13 @@
 
 import random
 
-import pytest
 
-
-@pytest.fixture(scope="function")
-def empty_vector_db_registry(llama_stack_client):
-    vector_dbs = [
-        vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list()
-    ]
-    for vector_db_id in vector_dbs:
-        llama_stack_client.vector_dbs.unregister(vector_db_id=vector_db_id)
-
-
-@pytest.fixture(scope="function")
-def single_entry_vector_db_registry(llama_stack_client, empty_vector_db_registry):
-    vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}"
-    llama_stack_client.vector_dbs.register(
-        vector_db_id=vector_db_id,
-        embedding_model="all-MiniLM-L6-v2",
-        embedding_dimension=384,
-        provider_id="faiss",
-    )
-    vector_dbs = [
-        vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list()
-    ]
-    return vector_dbs
-
-
-def test_vector_db_retrieve(llama_stack_client, empty_vector_db_registry):
+def test_vector_db_retrieve(llama_stack_client, embedding_model):
     # Register a memory bank first
     vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}"
     llama_stack_client.vector_dbs.register(
         vector_db_id=vector_db_id,
-        embedding_model="all-MiniLM-L6-v2",
+        embedding_model=embedding_model,
         embedding_dimension=384,
         provider_id="faiss",
     )
@@ -47,23 +21,23 @@ def test_vector_db_retrieve(llama_stack_client, empty_vector_db_registry):
     response = llama_stack_client.vector_dbs.retrieve(vector_db_id=vector_db_id)
     assert response is not None
     assert response.identifier == vector_db_id
-    assert response.embedding_model == "all-MiniLM-L6-v2"
+    assert response.embedding_model == embedding_model
     assert response.provider_id == "faiss"
     assert response.provider_resource_id == vector_db_id
 
 
-def test_vector_db_list(llama_stack_client, empty_vector_db_registry):
+def test_vector_db_list(llama_stack_client):
     vector_dbs_after_register = [
         vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list()
     ]
     assert len(vector_dbs_after_register) == 0
 
 
-def test_vector_db_register(llama_stack_client, empty_vector_db_registry):
+def test_vector_db_register(llama_stack_client, embedding_model):
     vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}"
     llama_stack_client.vector_dbs.register(
         vector_db_id=vector_db_id,
-        embedding_model="all-MiniLM-L6-v2",
+        embedding_model=embedding_model,
         embedding_dimension=384,
         provider_id="faiss",
     )
@@ -74,7 +48,7 @@ def test_vector_db_register(llama_stack_client, empty_vector_db_registry):
     assert vector_dbs_after_register == [vector_db_id]
 
 
-def test_vector_db_unregister(llama_stack_client, single_entry_vector_db_registry):
+def test_vector_db_unregister(llama_stack_client):
     vector_dbs = [
         vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list()
     ]

From 08dcb9e31e41bb7984b1a54a74ffa6622b733e20 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 16:42:36 -0800
Subject: [PATCH 084/107] Accept "query_config" params for the RAG tool

---
 .../agents/meta_reference/agent_instance.py   | 24 +++++++++----------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py
index 1b375fba7..a57b989a0 100644
--- a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py
+++ b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py
@@ -18,6 +18,7 @@ from urllib.parse import urlparse
 
 import httpx
 from llama_models.llama3.api.datatypes import BuiltinTool, ToolCall, ToolParamDefinition
+from pydantic import TypeAdapter
 
 from llama_stack.apis.agents import (
     AgentConfig,
@@ -60,13 +61,7 @@ from llama_stack.apis.inference import (
     UserMessage,
 )
 from llama_stack.apis.safety import Safety
-from llama_stack.apis.tools import (
-    DefaultRAGQueryGeneratorConfig,
-    RAGDocument,
-    RAGQueryConfig,
-    ToolGroups,
-    ToolRuntime,
-)
+from llama_stack.apis.tools import RAGDocument, RAGQueryConfig, ToolGroups, ToolRuntime
 from llama_stack.apis.vector_io import VectorIO
 from llama_stack.providers.utils.kvstore import KVStore
 from llama_stack.providers.utils.memory.vector_store import concat_interleaved_content
@@ -410,6 +405,15 @@ class ChatAgent(ShieldRunnerMixin):
 
                 args = toolgroup_args.get(MEMORY_GROUP, {})
                 vector_db_ids = args.get("vector_db_ids", [])
+                query_config = args.get("query_config")
+                if query_config:
+                    query_config = TypeAdapter(RAGQueryConfig).validate_python(
+                        query_config
+                    )
+                else:
+                    # handle someone passing an empty dict
+                    query_config = RAGQueryConfig()
+
                 session_info = await self.storage.get_session_info(session_id)
 
                 # if the session has a memory bank id, let the memory tool use it
@@ -437,11 +441,7 @@ class ChatAgent(ShieldRunnerMixin):
                         [msg.content for msg in input_messages]
                     ),
                     vector_db_ids=vector_db_ids,
-                    query_config=RAGQueryConfig(
-                        query_generator_config=DefaultRAGQueryGeneratorConfig(),
-                        max_tokens_in_context=4096,
-                        max_chunks=5,
-                    ),
+                    query_config=query_config,
                 )
                 retrieved_context = result.content
 

From a8345f5f76e70abadd661dbc0bd21fec6c00e9c2 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 16:53:54 -0800
Subject: [PATCH 085/107] Fix llama stack build docker creation to have correct
 entrypoint

---
 llama_stack/distribution/build.py           |  5 ++++-
 llama_stack/distribution/build_container.sh | 12 +++++++-----
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/llama_stack/distribution/build.py b/llama_stack/distribution/build.py
index b8d35ccdc..950338730 100644
--- a/llama_stack/distribution/build.py
+++ b/llama_stack/distribution/build.py
@@ -119,12 +119,15 @@ def build_image(
     normal_deps += SERVER_DEPENDENCIES
 
     if build_config.image_type == ImageType.container.value:
+        if not template_name:
+            raise ValueError("template_name is required for container builds")
+
         script = str(
             importlib.resources.files("llama_stack") / "distribution/build_container.sh"
         )
         args = [
             script,
-            image_name,
+            template_name,
             container_image,
             str(build_file_path),
             str(BUILDS_BASE_DIR / ImageType.container.value),
diff --git a/llama_stack/distribution/build_container.sh b/llama_stack/distribution/build_container.sh
index c7b6211f7..91c1dd1a6 100755
--- a/llama_stack/distribution/build_container.sh
+++ b/llama_stack/distribution/build_container.sh
@@ -12,9 +12,10 @@ TEST_PYPI_VERSION=${TEST_PYPI_VERSION:-}
 PYPI_VERSION=${PYPI_VERSION:-}
 BUILD_PLATFORM=${BUILD_PLATFORM:-}
 
-if [ "$#" -lt 4 ]; then
-  echo "Usage: $0    []" >&2
-  echo "Example: $0 my-fastapi-app python:3.9-slim 'fastapi uvicorn' " >&2
+if [ "$#" -lt 5 ]; then
+  # This only works for templates
+  echo "Usage: $0     []" >&2
+  echo "Example: $0 fireworks python:3.9-slim 'fastapi uvicorn' /path/to/build/dir" >&2
   exit 1
 fi
 
@@ -22,7 +23,7 @@ special_pip_deps="$6"
 
 set -euo pipefail
 
-build_name="$1"
+template_name="$1"
 container_base=$2
 build_file_path=$3
 host_build_dir=$4
@@ -151,7 +152,7 @@ add_to_container << EOF
 # This would be good in production but for debugging flexibility lets not add it right now
 # We need a more solid production ready entrypoint.sh anyway
 #
-ENTRYPOINT ["python", "-m", "llama_stack.distribution.server.server", "--template", "$build_name"]
+ENTRYPOINT ["python", "-m", "llama_stack.distribution.server.server", "--template", "$template_name"]
 
 EOF
 
@@ -183,6 +184,7 @@ else
 fi
 
 # Add version tag to image name
+build_name="distribution-$template_name"
 image_tag="$build_name:$version_tag"
 
 # Detect platform architecture

From 72a1b27d01d7c7ec644a85f42d06b9df7dadc913 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 18:09:46 -0800
Subject: [PATCH 086/107] nitpick

---
 tests/client-sdk/safety/conftest.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/client-sdk/safety/conftest.py b/tests/client-sdk/safety/conftest.py
index 9c5ff7352..c4570801c 100644
--- a/tests/client-sdk/safety/conftest.py
+++ b/tests/client-sdk/safety/conftest.py
@@ -7,7 +7,7 @@
 
 def pytest_addoption(parser):
     parser.addoption(
-        "--safety_shield",
+        "--safety-shield",
         action="store",
         default="meta-llama/Llama-Guard-3-1B",
         help="Specify the safety shield model to use for testing",
@@ -18,5 +18,5 @@ def pytest_generate_tests(metafunc):
     if "llama_guard_text_shield_id" in metafunc.fixturenames:
         metafunc.parametrize(
             "llama_guard_text_shield_id",
-            [metafunc.config.getoption("--safety_shield")],
+            [metafunc.config.getoption("--safety-shield")],
         )

From f4b0f2af8bf91fcc109d17d55c235296c71aefd1 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 18:11:42 -0800
Subject: [PATCH 087/107] If initialization fails for library client, error the
 test

---
 tests/client-sdk/conftest.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tests/client-sdk/conftest.py b/tests/client-sdk/conftest.py
index 0f0733010..779c10e21 100644
--- a/tests/client-sdk/conftest.py
+++ b/tests/client-sdk/conftest.py
@@ -64,7 +64,9 @@ def llama_stack_client(provider_data):
             provider_data=provider_data,
             skip_logger_removal=True,
         )
-        client.initialize()
+        if not client.initialize():
+            raise RuntimeError("Initialization failed")
+
     elif os.environ.get("LLAMA_STACK_BASE_URL"):
         client = LlamaStackClient(
             base_url=get_env_or_fail("LLAMA_STACK_BASE_URL"),

From 23f1980f9cb4390295254b3a43fc73a7eaacb2bd Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 18:31:59 -0800
Subject: [PATCH 088/107] Fix meta-reference GPU implementation for inference

---
 .../providers/inline/inference/meta_reference/parallel_utils.py | 2 +-
 tests/client-sdk/inference/test_inference.py                    | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/llama_stack/providers/inline/inference/meta_reference/parallel_utils.py b/llama_stack/providers/inline/inference/meta_reference/parallel_utils.py
index 36720612c..ced712257 100644
--- a/llama_stack/providers/inline/inference/meta_reference/parallel_utils.py
+++ b/llama_stack/providers/inline/inference/meta_reference/parallel_utils.py
@@ -357,8 +357,8 @@ class ModelParallelProcessGroup:
         assert not self.running, "inference already running"
 
         self.running = True
-        self.request_socket.send(encode_msg(TaskRequest(task=req)))
         try:
+            self.request_socket.send(encode_msg(TaskRequest(task=req)))
             while True:
                 obj_json = self.request_socket.recv()
                 obj = parse_message(obj_json)
diff --git a/tests/client-sdk/inference/test_inference.py b/tests/client-sdk/inference/test_inference.py
index 01bbd7dc0..8ca11521c 100644
--- a/tests/client-sdk/inference/test_inference.py
+++ b/tests/client-sdk/inference/test_inference.py
@@ -54,7 +54,7 @@ def base64_image_url():
     with open(image_path, "rb") as image_file:
         # Convert the image to base64
         base64_string = base64.b64encode(image_file.read()).decode("utf-8")
-        base64_url = f"data:image;base64,{base64_string}"
+        base64_url = f"data:image/png;base64,{base64_string}"
         return base64_url
 
 

From 597869a2aa46d70a96ef60aee266d66c57d0ed8e Mon Sep 17 00:00:00 2001
From: Sixian Yi 
Date: Wed, 22 Jan 2025 19:20:49 -0800
Subject: [PATCH 089/107] add distro report (#847)

# What does this PR do?

Generate distro reports to cover inference, agents, and vector_io.


## Test Plan

Report generated through `/opt/miniconda3/envs/stack/bin/pytest -s -v
tests/client-sdk/ --report`


## Sources

Please link relevant resources if necessary.


## Before submitting

- [ ] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 llama_stack/templates/cerebras/report.md     |  44 ++++++++
 llama_stack/templates/fireworks/report.md    |  57 +++++-----
 llama_stack/templates/ollama/report.md       |  44 ++++++++
 llama_stack/templates/tgi/report.md          |  44 ++++++++
 llama_stack/templates/together/report.md     |  44 ++++++++
 tests/client-sdk/metadata.py                 |  14 +--
 tests/client-sdk/report.py                   | 103 +++++++++++++++----
 tests/client-sdk/vector_io/test_vector_io.py |  38 ++++++-
 8 files changed, 328 insertions(+), 60 deletions(-)
 create mode 100644 llama_stack/templates/cerebras/report.md
 create mode 100644 llama_stack/templates/ollama/report.md
 create mode 100644 llama_stack/templates/tgi/report.md
 create mode 100644 llama_stack/templates/together/report.md

diff --git a/llama_stack/templates/cerebras/report.md b/llama_stack/templates/cerebras/report.md
new file mode 100644
index 000000000..c65cd4979
--- /dev/null
+++ b/llama_stack/templates/cerebras/report.md
@@ -0,0 +1,44 @@
+# Report for cerebras distribution
+
+## Supported Models:
+| Model Descriptor | cerebras |
+|:---|:---|
+| meta-llama/Llama-3-8B-Instruct | ❌ |
+| meta-llama/Llama-3-70B-Instruct | ❌ |
+| meta-llama/Llama-3.1-8B-Instruct | ✅ |
+| meta-llama/Llama-3.1-70B-Instruct | ❌ |
+| meta-llama/Llama-3.1-405B-Instruct-FP8 | ❌ |
+| meta-llama/Llama-3.2-1B-Instruct | ❌ |
+| meta-llama/Llama-3.2-3B-Instruct | ❌ |
+| meta-llama/Llama-3.2-11B-Vision-Instruct | ❌ |
+| meta-llama/Llama-3.2-90B-Vision-Instruct | ❌ |
+| meta-llama/Llama-3.3-70B-Instruct | ✅ |
+| meta-llama/Llama-Guard-3-11B-Vision | ❌ |
+| meta-llama/Llama-Guard-3-1B | ❌ |
+| meta-llama/Llama-Guard-3-8B | ❌ |
+| meta-llama/Llama-Guard-2-8B | ❌ |
+
+## Inference:
+| Model | API | Capability | Test | Status |
+|:----- |:-----|:-----|:-----|:-----|
+| Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | streaming | test_image_chat_completion_streaming | ❌ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | ❌ |
+| Llama-3.1-8B-Instruct | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | streaming | test_text_completion_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ❌ |
+
+## Vector_io:
+| API | Capability | Test | Status |
+|:-----|:-----|:-----|:-----|
+| /retrieve |  | test_vector_db_retrieve | ✅ |
+
+## Agents:
+| API | Capability | Test | Status |
+|:-----|:-----|:-----|:-----|
+| /create_agent_turn | rag | test_rag_agent | ✅ |
+| /create_agent_turn | custom_tool | test_custom_tool | ❌ |
+| /create_agent_turn | code_execution | test_code_interpreter_for_attachments | ✅ |
diff --git a/llama_stack/templates/fireworks/report.md b/llama_stack/templates/fireworks/report.md
index 55efec0f5..1c5550bf4 100644
--- a/llama_stack/templates/fireworks/report.md
+++ b/llama_stack/templates/fireworks/report.md
@@ -3,43 +3,42 @@
 ## Supported Models:
 | Model Descriptor | fireworks |
 |:---|:---|
-| meta-llama/Llama-3-8B-Instruct | ❌ |
-| meta-llama/Llama-3-70B-Instruct | ❌ |
-| meta-llama/Llama-3.1-8B-Instruct | ✅ |
-| meta-llama/Llama-3.1-70B-Instruct | ✅ |
-| meta-llama/Llama-3.1-405B-Instruct-FP8 | ✅ |
-| meta-llama/Llama-3.2-1B-Instruct | ✅ |
-| meta-llama/Llama-3.2-3B-Instruct | ✅ |
-| meta-llama/Llama-3.2-11B-Vision-Instruct | ✅ |
-| meta-llama/Llama-3.2-90B-Vision-Instruct | ✅ |
-| meta-llama/Llama-3.3-70B-Instruct | ✅ |
-| meta-llama/Llama-Guard-3-11B-Vision | ✅ |
-| meta-llama/Llama-Guard-3-1B | ❌ |
-| meta-llama/Llama-Guard-3-8B | ✅ |
-| meta-llama/Llama-Guard-2-8B | ❌ |
+| Llama-3-8B-Instruct | ❌ |
+| Llama-3-70B-Instruct | ❌ |
+| Llama3.1-8B-Instruct | ✅ |
+| Llama3.1-70B-Instruct | ✅ |
+| Llama3.1-405B-Instruct | ✅ |
+| Llama3.2-1B-Instruct | ✅ |
+| Llama3.2-3B-Instruct | ✅ |
+| Llama3.2-11B-Vision-Instruct | ✅ |
+| Llama3.2-90B-Vision-Instruct | ✅ |
+| Llama3.3-70B-Instruct | ✅ |
+| Llama-Guard-3-11B-Vision | ✅ |
+| Llama-Guard-3-1B | ❌ |
+| Llama-Guard-3-8B | ✅ |
+| Llama-Guard-2-8B | ❌ |
 
 ## Inference:
 | Model | API | Capability | Test | Status |
 |:----- |:-----|:-----|:-----|:-----|
-| Text | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ |
-| Vision | /chat_completion | streaming | test_image_chat_completion_streaming | ✅ |
-| Vision | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | ✅ |
-| Text | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ✅ |
-| Text | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ✅ |
-| Text | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ✅ |
-| Text | /completion | streaming | test_text_completion_streaming | ✅ |
-| Text | /completion | non_streaming | test_text_completion_non_streaming | ✅ |
-| Text | /completion | structured_output | test_text_completion_structured_output | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | streaming | test_image_chat_completion_streaming | ✅ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | streaming | test_text_completion_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ✅ |
 
-## Memory:
+## Vector_io:
 | API | Capability | Test | Status |
 |:-----|:-----|:-----|:-----|
-| /insert, /query | inline | test_memory_bank_insert_inline_and_query | ✅ |
-| /insert, /query | url | test_memory_bank_insert_from_url_and_query | ✅ |
+| /retrieve |  | test_vector_db_retrieve | ✅ |
 
 ## Agents:
 | API | Capability | Test | Status |
 |:-----|:-----|:-----|:-----|
-| create_agent_turn | rag | test_rag_agent | ✅ |
-| create_agent_turn | custom_tool | test_custom_tool | ✅ |
-| create_agent_turn | code_execution | test_code_execution | ❌ |
+| /create_agent_turn | rag | test_rag_agent | ✅ |
+| /create_agent_turn | custom_tool | test_custom_tool | ✅ |
+| /create_agent_turn | code_execution | test_code_interpreter_for_attachments | ✅ |
diff --git a/llama_stack/templates/ollama/report.md b/llama_stack/templates/ollama/report.md
new file mode 100644
index 000000000..0d370b8ec
--- /dev/null
+++ b/llama_stack/templates/ollama/report.md
@@ -0,0 +1,44 @@
+# Report for ollama distribution
+
+## Supported Models:
+| Model Descriptor | ollama |
+|:---|:---|
+| Llama-3-8B-Instruct | ❌ |
+| Llama-3-70B-Instruct | ❌ |
+| Llama3.1-8B-Instruct | ✅ |
+| Llama3.1-70B-Instruct | ✅ |
+| Llama3.1-405B-Instruct | ✅ |
+| Llama3.2-1B-Instruct | ✅ |
+| Llama3.2-3B-Instruct | ✅ |
+| Llama3.2-11B-Vision-Instruct | ✅ |
+| Llama3.2-90B-Vision-Instruct | ✅ |
+| Llama3.3-70B-Instruct | ✅ |
+| Llama-Guard-3-11B-Vision | ❌ |
+| Llama-Guard-3-1B | ✅ |
+| Llama-Guard-3-8B | ✅ |
+| Llama-Guard-2-8B | ❌ |
+
+## Inference:
+| Model | API | Capability | Test | Status |
+|:----- |:-----|:-----|:-----|:-----|
+| Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | streaming | test_image_chat_completion_streaming | ❌ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | ❌ |
+| Llama-3.1-8B-Instruct | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | streaming | test_text_completion_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ✅ |
+
+## Vector_io:
+| API | Capability | Test | Status |
+|:-----|:-----|:-----|:-----|
+| /retrieve |  | test_vector_db_retrieve | ✅ |
+
+## Agents:
+| API | Capability | Test | Status |
+|:-----|:-----|:-----|:-----|
+| /create_agent_turn | rag | test_rag_agent | ✅ |
+| /create_agent_turn | custom_tool | test_custom_tool | ✅ |
+| /create_agent_turn | code_execution | test_code_interpreter_for_attachments | ✅ |
diff --git a/llama_stack/templates/tgi/report.md b/llama_stack/templates/tgi/report.md
new file mode 100644
index 000000000..1f76ff692
--- /dev/null
+++ b/llama_stack/templates/tgi/report.md
@@ -0,0 +1,44 @@
+# Report for tgi distribution
+
+## Supported Models:
+| Model Descriptor | tgi |
+|:---|:---|
+| Llama-3-8B-Instruct | ✅ |
+| Llama-3-70B-Instruct | ✅ |
+| Llama3.1-8B-Instruct | ✅ |
+| Llama3.1-70B-Instruct | ✅ |
+| Llama3.1-405B-Instruct | ✅ |
+| Llama3.2-1B-Instruct | ✅ |
+| Llama3.2-3B-Instruct | ✅ |
+| Llama3.2-11B-Vision-Instruct | ✅ |
+| Llama3.2-90B-Vision-Instruct | ✅ |
+| Llama3.3-70B-Instruct | ✅ |
+| Llama-Guard-3-11B-Vision | ✅ |
+| Llama-Guard-3-1B | ✅ |
+| Llama-Guard-3-8B | ✅ |
+| Llama-Guard-2-8B | ✅ |
+
+## Inference:
+| Model | API | Capability | Test | Status |
+|:----- |:-----|:-----|:-----|:-----|
+| Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | streaming | test_image_chat_completion_streaming | ❌ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | ❌ |
+| Llama-3.1-8B-Instruct | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | streaming | test_text_completion_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ✅ |
+
+## Vector_io:
+| API | Capability | Test | Status |
+|:-----|:-----|:-----|:-----|
+| /retrieve |  | test_vector_db_retrieve | ✅ |
+
+## Agents:
+| API | Capability | Test | Status |
+|:-----|:-----|:-----|:-----|
+| /create_agent_turn | rag | test_rag_agent | ✅ |
+| /create_agent_turn | custom_tool | test_custom_tool | ✅ |
+| /create_agent_turn | code_execution | test_code_interpreter_for_attachments | ✅ |
diff --git a/llama_stack/templates/together/report.md b/llama_stack/templates/together/report.md
new file mode 100644
index 000000000..10891f4e5
--- /dev/null
+++ b/llama_stack/templates/together/report.md
@@ -0,0 +1,44 @@
+# Report for together distribution
+
+## Supported Models:
+| Model Descriptor | together |
+|:---|:---|
+| Llama-3-8B-Instruct | ❌ |
+| Llama-3-70B-Instruct | ❌ |
+| Llama3.1-8B-Instruct | ✅ |
+| Llama3.1-70B-Instruct | ✅ |
+| Llama3.1-405B-Instruct | ✅ |
+| Llama3.2-1B-Instruct | ❌ |
+| Llama3.2-3B-Instruct | ✅ |
+| Llama3.2-11B-Vision-Instruct | ✅ |
+| Llama3.2-90B-Vision-Instruct | ✅ |
+| Llama3.3-70B-Instruct | ✅ |
+| Llama-Guard-3-11B-Vision | ✅ |
+| Llama-Guard-3-1B | ❌ |
+| Llama-Guard-3-8B | ✅ |
+| Llama-Guard-2-8B | ❌ |
+
+## Inference:
+| Model | API | Capability | Test | Status |
+|:----- |:-----|:-----|:-----|:-----|
+| Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | streaming | test_image_chat_completion_streaming | ✅ |
+| Llama-3.2-11B-Vision-Instruct | /chat_completion | non_streaming | test_image_chat_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | non_streaming | test_text_chat_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /chat_completion | tool_calling | test_text_chat_completion_with_tool_calling_and_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | streaming | test_text_completion_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ |
+| Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ✅ |
+
+## Vector_io:
+| API | Capability | Test | Status |
+|:-----|:-----|:-----|:-----|
+| /retrieve |  | test_vector_db_retrieve | ✅ |
+
+## Agents:
+| API | Capability | Test | Status |
+|:-----|:-----|:-----|:-----|
+| /create_agent_turn | rag | test_rag_agent | ✅ |
+| /create_agent_turn | custom_tool | test_custom_tool | ✅ |
+| /create_agent_turn | code_execution | test_code_interpreter_for_attachments | ✅ |
diff --git a/tests/client-sdk/metadata.py b/tests/client-sdk/metadata.py
index 1a87c6bd0..badd7edff 100644
--- a/tests/client-sdk/metadata.py
+++ b/tests/client-sdk/metadata.py
@@ -4,6 +4,7 @@
 # This source code is licensed under the terms described in the LICENSE file in
 # the root directory of this source tree.
 
+from llama_stack.providers.datatypes import Api
 
 INFERENCE_API_CAPA_TEST_MAP = {
     "chat_completion": {
@@ -27,10 +28,9 @@ INFERENCE_API_CAPA_TEST_MAP = {
     },
 }
 
-MEMORY_API_TEST_MAP = {
-    "/insert, /query": {
-        "inline": ["test_memory_bank_insert_inline_and_query"],
-        "url": ["test_memory_bank_insert_from_url_and_query"],
+VECTORIO_API_TEST_MAP = {
+    "retrieve": {
+        "": ["test_vector_db_retrieve"],
     }
 }
 
@@ -44,7 +44,7 @@ AGENTS_API_TEST_MAP = {
 
 
 API_MAPS = {
-    "inference": INFERENCE_API_CAPA_TEST_MAP,
-    "memory": MEMORY_API_TEST_MAP,
-    "agents": AGENTS_API_TEST_MAP,
+    Api.inference: INFERENCE_API_CAPA_TEST_MAP,
+    Api.vector_io: VECTORIO_API_TEST_MAP,
+    Api.agents: AGENTS_API_TEST_MAP,
 }
diff --git a/tests/client-sdk/report.py b/tests/client-sdk/report.py
index 5a291f1af..de50efa46 100644
--- a/tests/client-sdk/report.py
+++ b/tests/client-sdk/report.py
@@ -12,8 +12,9 @@ from pathlib import Path
 from urllib.parse import urlparse
 
 import pytest
-
+from llama_models.datatypes import CoreModelId
 from llama_models.sku_list import (
+    all_registered_models,
     llama3_1_instruct_models,
     llama3_2_instruct_models,
     llama3_3_instruct_models,
@@ -22,6 +23,7 @@ from llama_models.sku_list import (
 )
 
 from llama_stack.distribution.library_client import LlamaStackAsLibraryClient
+from llama_stack.providers.datatypes import Api
 from llama_stack.providers.tests.env import get_env_or_fail
 
 from llama_stack_client import LlamaStackClient
@@ -42,6 +44,45 @@ def featured_models_repo_names():
     return [model.huggingface_repo for model in models if not model.variant]
 
 
+SUPPORTED_MODELS = {
+    "ollama": set(
+        [
+            CoreModelId.llama3_1_8b_instruct.value,
+            CoreModelId.llama3_1_8b_instruct.value,
+            CoreModelId.llama3_1_70b_instruct.value,
+            CoreModelId.llama3_1_70b_instruct.value,
+            CoreModelId.llama3_1_405b_instruct.value,
+            CoreModelId.llama3_1_405b_instruct.value,
+            CoreModelId.llama3_2_1b_instruct.value,
+            CoreModelId.llama3_2_1b_instruct.value,
+            CoreModelId.llama3_2_3b_instruct.value,
+            CoreModelId.llama3_2_3b_instruct.value,
+            CoreModelId.llama3_2_11b_vision_instruct.value,
+            CoreModelId.llama3_2_11b_vision_instruct.value,
+            CoreModelId.llama3_2_90b_vision_instruct.value,
+            CoreModelId.llama3_2_90b_vision_instruct.value,
+            CoreModelId.llama3_3_70b_instruct.value,
+            CoreModelId.llama_guard_3_8b.value,
+            CoreModelId.llama_guard_3_1b.value,
+        ]
+    ),
+    "tgi": set(
+        [
+            model.core_model_id.value
+            for model in all_registered_models()
+            if model.huggingface_repo
+        ]
+    ),
+    "vllm": set(
+        [
+            model.core_model_id.value
+            for model in all_registered_models()
+            if model.huggingface_repo
+        ]
+    ),
+}
+
+
 class Report:
 
     def __init__(self):
@@ -90,6 +131,8 @@ class Report:
         # test function -> test nodeid
         self.test_data = dict()
         self.test_name_to_nodeid = defaultdict(list)
+        self.vision_model_id = None
+        self.text_model_id = None
 
     @pytest.hookimpl(tryfirst=True)
     def pytest_runtest_logreport(self, report):
@@ -113,20 +156,28 @@ class Report:
         report.append(dividor)
 
         rows = []
-
-        try:
+        if self.image_name in SUPPORTED_MODELS:
+            for model in all_registered_models():
+                if (
+                    "Instruct" not in model.core_model_id.value
+                    and "Guard" not in model.core_model_id.value
+                ) or (model.variant):
+                    continue
+                row = f"| {model.core_model_id.value} |"
+                if model.core_model_id.value in SUPPORTED_MODELS[self.image_name]:
+                    row += " ✅ |"
+                else:
+                    row += " ❌ |"
+                rows.append(row)
+        else:
             supported_models = {m.identifier for m in self.client.models.list()}
-        except Exception as e:
-            cprint(f"Error getting models: {e}", "red")
-            supported_models = set()
-
-        for m_name in featured_models_repo_names():
-            row = f"| {m_name} |"
-            if m_name in supported_models:
-                row += " ✅ |"
-            else:
-                row += " ❌ |"
-            rows.append(row)
+            for model in featured_models_repo_names():
+                row = f"| {model} |"
+                if model in supported_models:
+                    row += " ✅ |"
+                else:
+                    row += " ❌ |"
+                rows.append(row)
         report.extend(rows)
 
         report.append("\n## Inference:")
@@ -134,23 +185,28 @@ class Report:
             "| Model | API | Capability | Test | Status |",
             "|:----- |:-----|:-----|:-----|:-----|",
         ]
-        for api, capa_map in API_MAPS["inference"].items():
+        for api, capa_map in API_MAPS[Api.inference].items():
             for capa, tests in capa_map.items():
                 for test_name in tests:
-                    model_type = "Text" if "text" in test_name else "Vision"
+                    model_id = (
+                        self.text_model_id
+                        if "text" in test_name
+                        else self.vision_model_id
+                    )
                     test_nodeids = self.test_name_to_nodeid[test_name]
                     assert len(test_nodeids) > 0
+
                     # There might be more than one parametrizations for the same test function. We take
                     # the result of the first one for now. Ideally we should mark the test as failed if
                     # any of the parametrizations failed.
                     test_table.append(
-                        f"| {model_type} | /{api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |"
+                        f"| {model_id} | /{api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |"
                     )
 
         report.extend(test_table)
 
-        for api_group in ["memory", "agents"]:
-            api_capitalized = api_group.capitalize()
+        for api_group in [Api.vector_io, Api.agents]:
+            api_capitalized = api_group.name.capitalize()
             report.append(f"\n## {api_capitalized}:")
             test_table = [
                 "| API | Capability | Test | Status |",
@@ -162,7 +218,7 @@ class Report:
                         test_nodeids = self.test_name_to_nodeid[test_name]
                         assert len(test_nodeids) > 0
                         test_table.append(
-                            f"| {api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |"
+                            f"| /{api} | {capa} | {test_name} | {self._print_result_icon(self.test_data[test_nodeids[0]])} |"
                         )
             report.extend(test_table)
 
@@ -173,6 +229,13 @@ class Report:
 
     def pytest_runtest_makereport(self, item, call):
         func_name = getattr(item, "originalname", item.name)
+        if "text_model_id" in item.funcargs:
+            text_model = item.funcargs["text_model_id"].split("/")[1]
+            self.text_model_id = self.text_model_id or text_model
+        elif "vision_model_id" in item.funcargs:
+            vision_model = item.funcargs["vision_model_id"].split("/")[1]
+            self.vision_model_id = self.vision_model_id or vision_model
+
         self.test_name_to_nodeid[func_name].append(item.nodeid)
 
     def _print_result_icon(self, result):
diff --git a/tests/client-sdk/vector_io/test_vector_io.py b/tests/client-sdk/vector_io/test_vector_io.py
index 20e49d805..2a110b73a 100644
--- a/tests/client-sdk/vector_io/test_vector_io.py
+++ b/tests/client-sdk/vector_io/test_vector_io.py
@@ -6,8 +6,36 @@
 
 import random
 
+import pytest
 
-def test_vector_db_retrieve(llama_stack_client, embedding_model):
+
+@pytest.fixture(scope="function")
+def empty_vector_db_registry(llama_stack_client):
+    vector_dbs = [
+        vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list()
+    ]
+    for vector_db_id in vector_dbs:
+        llama_stack_client.vector_dbs.unregister(vector_db_id=vector_db_id)
+
+
+@pytest.fixture(scope="function")
+def single_entry_vector_db_registry(llama_stack_client, empty_vector_db_registry):
+    vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}"
+    llama_stack_client.vector_dbs.register(
+        vector_db_id=vector_db_id,
+        embedding_model="all-MiniLM-L6-v2",
+        embedding_dimension=384,
+        provider_id="faiss",
+    )
+    vector_dbs = [
+        vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list()
+    ]
+    return vector_dbs
+
+
+def test_vector_db_retrieve(
+    llama_stack_client, embedding_model, empty_vector_db_registry
+):
     # Register a memory bank first
     vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}"
     llama_stack_client.vector_dbs.register(
@@ -26,14 +54,16 @@ def test_vector_db_retrieve(llama_stack_client, embedding_model):
     assert response.provider_resource_id == vector_db_id
 
 
-def test_vector_db_list(llama_stack_client):
+def test_vector_db_list(llama_stack_client, empty_vector_db_registry):
     vector_dbs_after_register = [
         vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list()
     ]
     assert len(vector_dbs_after_register) == 0
 
 
-def test_vector_db_register(llama_stack_client, embedding_model):
+def test_vector_db_register(
+    llama_stack_client, embedding_model, empty_vector_db_registry
+):
     vector_db_id = f"test_vector_db_{random.randint(1000, 9999)}"
     llama_stack_client.vector_dbs.register(
         vector_db_id=vector_db_id,
@@ -48,7 +78,7 @@ def test_vector_db_register(llama_stack_client, embedding_model):
     assert vector_dbs_after_register == [vector_db_id]
 
 
-def test_vector_db_unregister(llama_stack_client):
+def test_vector_db_unregister(llama_stack_client, single_entry_vector_db_registry):
     vector_dbs = [
         vector_db.identifier for vector_db in llama_stack_client.vector_dbs.list()
     ]

From f3d8864c36ee98143d2c6abc58ce199612883644 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 20:22:51 -0800
Subject: [PATCH 090/107] Rename builtin::memory -> builtin::rag

---
 .../Llama_Stack_Benchmark_Evals.ipynb         | 16 +++++-----
 ...Llama_Stack_Building_AI_Applications.ipynb | 30 +++++++++----------
 .../remote_hosted_distro/nvidia.md            |  2 +-
 .../self_hosted_distro/bedrock.md             |  2 +-
 .../self_hosted_distro/cerebras.md            |  2 +-
 .../self_hosted_distro/fireworks.md           |  2 +-
 .../self_hosted_distro/meta-reference-gpu.md  |  2 +-
 .../meta-reference-quantized-gpu.md           |  2 +-
 .../self_hosted_distro/ollama.md              |  2 +-
 .../self_hosted_distro/remote-vllm.md         |  2 +-
 .../distributions/self_hosted_distro/tgi.md   |  2 +-
 .../self_hosted_distro/together.md            |  2 +-
 .../distribution/ui/page/playground/rag.py    |  2 +-
 .../agents/meta_reference/agent_instance.py   | 10 +++----
 .../meta_reference/tests/test_chat_agent.py   |  4 +--
 .../providers/registry/tool_runtime.py        |  2 +-
 .../providers/tests/agents/test_agents.py     |  2 +-
 llama_stack/providers/tests/tools/fixtures.py |  8 ++---
 llama_stack/templates/bedrock/bedrock.py      |  6 ++--
 llama_stack/templates/bedrock/build.yaml      |  2 +-
 llama_stack/templates/bedrock/run.yaml        |  8 ++---
 llama_stack/templates/cerebras/build.yaml     |  2 +-
 llama_stack/templates/cerebras/cerebras.py    |  6 ++--
 llama_stack/templates/cerebras/run.yaml       |  8 ++---
 llama_stack/templates/fireworks/build.yaml    |  2 +-
 llama_stack/templates/fireworks/fireworks.py  |  6 ++--
 .../templates/fireworks/run-with-safety.yaml  |  8 ++---
 llama_stack/templates/fireworks/run.yaml      |  8 ++---
 llama_stack/templates/hf-endpoint/build.yaml  |  2 +-
 .../templates/hf-endpoint/hf_endpoint.py      |  6 ++--
 .../hf-endpoint/run-with-safety.yaml          |  8 ++---
 llama_stack/templates/hf-endpoint/run.yaml    |  8 ++---
 .../templates/hf-serverless/build.yaml        |  2 +-
 .../templates/hf-serverless/hf_serverless.py  |  6 ++--
 .../hf-serverless/run-with-safety.yaml        |  8 ++---
 llama_stack/templates/hf-serverless/run.yaml  |  8 ++---
 .../templates/meta-reference-gpu/build.yaml   |  2 +-
 .../meta-reference-gpu/meta_reference.py      |  6 ++--
 .../meta-reference-gpu/run-with-safety.yaml   |  8 ++---
 .../templates/meta-reference-gpu/run.yaml     |  8 ++---
 .../meta-reference-quantized-gpu/build.yaml   |  2 +-
 .../meta_reference.py                         |  6 ++--
 .../meta-reference-quantized-gpu/run.yaml     |  8 ++---
 llama_stack/templates/nvidia/build.yaml       |  2 +-
 llama_stack/templates/nvidia/nvidia.py        |  6 ++--
 llama_stack/templates/nvidia/run.yaml         |  8 ++---
 llama_stack/templates/ollama/build.yaml       |  2 +-
 llama_stack/templates/ollama/ollama.py        |  6 ++--
 .../templates/ollama/run-with-safety.yaml     |  8 ++---
 llama_stack/templates/ollama/run.yaml         |  8 ++---
 llama_stack/templates/remote-vllm/build.yaml  |  2 +-
 .../remote-vllm/run-with-safety.yaml          |  8 ++---
 llama_stack/templates/remote-vllm/run.yaml    |  8 ++---
 llama_stack/templates/remote-vllm/vllm.py     |  6 ++--
 llama_stack/templates/tgi/build.yaml          |  2 +-
 .../templates/tgi/run-with-safety.yaml        |  8 ++---
 llama_stack/templates/tgi/run.yaml            |  8 ++---
 llama_stack/templates/tgi/tgi.py              |  6 ++--
 llama_stack/templates/together/build.yaml     |  2 +-
 .../templates/together/run-with-safety.yaml   |  8 ++---
 llama_stack/templates/together/run.yaml       |  8 ++---
 llama_stack/templates/together/together.py    |  6 ++--
 llama_stack/templates/vllm-gpu/build.yaml     |  2 +-
 llama_stack/templates/vllm-gpu/run.yaml       |  8 ++---
 llama_stack/templates/vllm-gpu/vllm.py        |  6 ++--
 tests/client-sdk/agents/test_agents.py        |  2 +-
 66 files changed, 184 insertions(+), 184 deletions(-)

diff --git a/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb b/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
index a552ce69d..61b5ab178 100644
--- a/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
+++ b/docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb
@@ -513,8 +513,8 @@
               "    provider_id: code-interpreter\n",
               "    provider_type: inline::code-interpreter\n",
               "  - config: {}\n",
-              "    provider_id: memory-runtime\n",
-              "    provider_type: inline::memory-runtime\n",
+              "    provider_id: rag-runtime\n",
+              "    provider_type: inline::rag-runtime\n",
               "scoring_fns: []\n",
               "shields:\n",
               "- params: null\n",
@@ -528,8 +528,8 @@
               "  toolgroup_id: builtin::websearch\n",
               "- args: null\n",
               "  mcp_endpoint: null\n",
-              "  provider_id: memory-runtime\n",
-              "  toolgroup_id: builtin::memory\n",
+              "  provider_id: rag-runtime\n",
+              "  toolgroup_id: builtin::rag\n",
               "- args: null\n",
               "  mcp_endpoint: null\n",
               "  provider_id: code-interpreter\n",
@@ -694,8 +694,8 @@
               "    provider_id: code-interpreter\n",
               "    provider_type: inlin\u001b[1;92me::c\u001b[0mode-interpreter\n",
               "  - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n",
-              "    provider_id: memory-runtime\n",
-              "    provider_type: inline::memory-runtime\n",
+              "    provider_id: rag-runtime\n",
+              "    provider_type: inline::rag-runtime\n",
               "scoring_fns: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n",
               "shields:\n",
               "- params: null\n",
@@ -709,8 +709,8 @@
               "  toolgroup_id: builtin::websearch\n",
               "- args: null\n",
               "  mcp_endpoint: null\n",
-              "  provider_id: memory-runtime\n",
-              "  toolgroup_id: builtin::memory\n",
+              "  provider_id: rag-runtime\n",
+              "  toolgroup_id: builtin::rag\n",
               "- args: null\n",
               "  mcp_endpoint: null\n",
               "  provider_id: code-interpreter\n",
diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
index daf37ab53..58b025db4 100644
--- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
+++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb
@@ -884,8 +884,8 @@
               "    provider_id: code-interpreter\n",
               "    provider_type: inline::code-interpreter\n",
               "  - config: {}\n",
-              "    provider_id: memory-runtime\n",
-              "    provider_type: inline::memory-runtime\n",
+              "    provider_id: rag-runtime\n",
+              "    provider_type: inline::rag-runtime\n",
               "  - config: {}\n",
               "    provider_id: model-context-protocol\n",
               "    provider_type: remote::model-context-protocol\n",
@@ -910,8 +910,8 @@
               "  toolgroup_id: builtin::websearch\n",
               "- args: null\n",
               "  mcp_endpoint: null\n",
-              "  provider_id: memory-runtime\n",
-              "  toolgroup_id: builtin::memory\n",
+              "  provider_id: rag-runtime\n",
+              "  toolgroup_id: builtin::rag\n",
               "- args: null\n",
               "  mcp_endpoint: null\n",
               "  provider_id: code-interpreter\n",
@@ -1068,8 +1068,8 @@
               "    provider_id: code-interpreter\n",
               "    provider_type: inlin\u001b[1;92me::c\u001b[0mode-interpreter\n",
               "  - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n",
-              "    provider_id: memory-runtime\n",
-              "    provider_type: inline::memory-runtime\n",
+              "    provider_id: rag-runtime\n",
+              "    provider_type: inline::rag-runtime\n",
               "  - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n",
               "    provider_id: model-context-protocol\n",
               "    provider_type: remote::model-context-protocol\n",
@@ -1094,8 +1094,8 @@
               "  toolgroup_id: builtin::websearch\n",
               "- args: null\n",
               "  mcp_endpoint: null\n",
-              "  provider_id: memory-runtime\n",
-              "  toolgroup_id: builtin::memory\n",
+              "  provider_id: rag-runtime\n",
+              "  toolgroup_id: builtin::rag\n",
               "- args: null\n",
               "  mcp_endpoint: null\n",
               "  provider_id: code-interpreter\n",
@@ -1804,9 +1804,9 @@
           "data": {
             "text/html": [
               "
ToolGroup(\n",
-              "identifier='builtin::memory',\n",
-              "provider_id='memory-runtime',\n",
-              "provider_resource_id='builtin::memory',\n",
+              "identifier='builtin::rag',\n",
+              "provider_id='rag-runtime',\n",
+              "provider_resource_id='builtin::rag',\n",
               "type='tool_group',\n",
               "args=None,\n",
               "mcp_endpoint=None\n",
@@ -1815,9 +1815,9 @@
             ],
             "text/plain": [
               "\u001b[1;35mToolGroup\u001b[0m\u001b[1m(\u001b[0m\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::memory'\u001b[0m,\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'memory-runtime'\u001b[0m,\n",
-              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::memory'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33midentifier\u001b[0m=\u001b[32m'builtin::rag'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_id\u001b[0m=\u001b[32m'rag-runtime'\u001b[0m,\n",
+              "\u001b[2;32m│   \u001b[0m\u001b[33mprovider_resource_id\u001b[0m=\u001b[32m'builtin::rag'\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mtype\u001b[0m=\u001b[32m'tool_group'\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33margs\u001b[0m=\u001b[3;35mNone\u001b[0m,\n",
               "\u001b[2;32m│   \u001b[0m\u001b[33mmcp_endpoint\u001b[0m=\u001b[3;35mNone\u001b[0m\n",
@@ -2118,7 +2118,7 @@
         "    enable_session_persistence=False,\n",
         "    toolgroups = [\n",
         "        {\n",
-        "          \"name\": \"builtin::memory\",\n",
+        "          \"name\": \"builtin::rag\",\n",
         "          \"args\" : {\n",
         "            \"vector_db_ids\": [vector_db_id],\n",
         "          }\n",
diff --git a/docs/source/distributions/remote_hosted_distro/nvidia.md b/docs/source/distributions/remote_hosted_distro/nvidia.md
index e4c3a155f..61b41b1d9 100644
--- a/docs/source/distributions/remote_hosted_distro/nvidia.md
+++ b/docs/source/distributions/remote_hosted_distro/nvidia.md
@@ -11,7 +11,7 @@ The `llamastack/distribution-nvidia` distribution consists of the following prov
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime`, `remote::model-context-protocol` |
 | vector_io | `inline::faiss` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/bedrock.md b/docs/source/distributions/self_hosted_distro/bedrock.md
index a66325560..f9a9f29cd 100644
--- a/docs/source/distributions/self_hosted_distro/bedrock.md
+++ b/docs/source/distributions/self_hosted_distro/bedrock.md
@@ -18,7 +18,7 @@ The `llamastack/distribution-bedrock` distribution consists of the following pro
 | safety | `remote::bedrock` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime`, `remote::model-context-protocol` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/cerebras.md b/docs/source/distributions/self_hosted_distro/cerebras.md
index 211082b7a..a44e6287a 100644
--- a/docs/source/distributions/self_hosted_distro/cerebras.md
+++ b/docs/source/distributions/self_hosted_distro/cerebras.md
@@ -11,7 +11,7 @@ The `llamastack/distribution-cerebras` distribution consists of the following pr
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/fireworks.md b/docs/source/distributions/self_hosted_distro/fireworks.md
index 39043b1c1..453cd746d 100644
--- a/docs/source/distributions/self_hosted_distro/fireworks.md
+++ b/docs/source/distributions/self_hosted_distro/fireworks.md
@@ -21,7 +21,7 @@ The `llamastack/distribution-fireworks` distribution consists of the following p
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime`, `remote::model-context-protocol` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md b/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md
index 8475aab3a..a371011fe 100644
--- a/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md
+++ b/docs/source/distributions/self_hosted_distro/meta-reference-gpu.md
@@ -21,7 +21,7 @@ The `llamastack/distribution-meta-reference-gpu` distribution consists of the fo
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime`, `remote::model-context-protocol` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md b/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md
index 6f1adb5a9..a32ccb65e 100644
--- a/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md
+++ b/docs/source/distributions/self_hosted_distro/meta-reference-quantized-gpu.md
@@ -21,7 +21,7 @@ The `llamastack/distribution-meta-reference-quantized-gpu` distribution consists
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime`, `remote::model-context-protocol` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/ollama.md b/docs/source/distributions/self_hosted_distro/ollama.md
index f5ba31feb..b03a5ee16 100644
--- a/docs/source/distributions/self_hosted_distro/ollama.md
+++ b/docs/source/distributions/self_hosted_distro/ollama.md
@@ -21,7 +21,7 @@ The `llamastack/distribution-ollama` distribution consists of the following prov
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/remote-vllm.md b/docs/source/distributions/self_hosted_distro/remote-vllm.md
index c2b3544d3..95dd392c1 100644
--- a/docs/source/distributions/self_hosted_distro/remote-vllm.md
+++ b/docs/source/distributions/self_hosted_distro/remote-vllm.md
@@ -20,7 +20,7 @@ The `llamastack/distribution-remote-vllm` distribution consists of the following
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime`, `remote::model-context-protocol` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/tgi.md b/docs/source/distributions/self_hosted_distro/tgi.md
index c21a6a586..1883b926c 100644
--- a/docs/source/distributions/self_hosted_distro/tgi.md
+++ b/docs/source/distributions/self_hosted_distro/tgi.md
@@ -22,7 +22,7 @@ The `llamastack/distribution-tgi` distribution consists of the following provide
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime`, `remote::model-context-protocol` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/docs/source/distributions/self_hosted_distro/together.md b/docs/source/distributions/self_hosted_distro/together.md
index 65a711522..2d5c8fc77 100644
--- a/docs/source/distributions/self_hosted_distro/together.md
+++ b/docs/source/distributions/self_hosted_distro/together.md
@@ -21,7 +21,7 @@ The `llamastack/distribution-together` distribution consists of the following pr
 | safety | `inline::llama-guard` |
 | scoring | `inline::basic`, `inline::llm-as-judge`, `inline::braintrust` |
 | telemetry | `inline::meta-reference` |
-| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::memory-runtime`, `remote::model-context-protocol` |
+| tool_runtime | `remote::brave-search`, `remote::tavily-search`, `inline::code-interpreter`, `inline::rag-runtime`, `remote::model-context-protocol` |
 | vector_io | `inline::faiss`, `remote::chromadb`, `remote::pgvector` |
 
 
diff --git a/llama_stack/distribution/ui/page/playground/rag.py b/llama_stack/distribution/ui/page/playground/rag.py
index 465e11560..49991dc54 100644
--- a/llama_stack/distribution/ui/page/playground/rag.py
+++ b/llama_stack/distribution/ui/page/playground/rag.py
@@ -135,7 +135,7 @@ def rag_chat_page():
         },
         toolgroups=[
             dict(
-                name="builtin::memory",
+                name="builtin::rag",
                 args={
                     "vector_db_ids": [
                         vector_db_id for vector_db_id in selected_vector_dbs
diff --git a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py
index a57b989a0..32801e514 100644
--- a/llama_stack/providers/inline/agents/meta_reference/agent_instance.py
+++ b/llama_stack/providers/inline/agents/meta_reference/agent_instance.py
@@ -81,7 +81,7 @@ def make_random_string(length: int = 8):
 TOOLS_ATTACHMENT_KEY_REGEX = re.compile(r"__tools_attachment__=(\{.*?\})")
 MEMORY_QUERY_TOOL = "query_from_memory"
 WEB_SEARCH_TOOL = "web_search"
-MEMORY_GROUP = "builtin::memory"
+RAG_TOOL_GROUP = "builtin::rag"
 
 
 class ChatAgent(ShieldRunnerMixin):
@@ -391,7 +391,7 @@ class ChatAgent(ShieldRunnerMixin):
                 session_id, documents, input_messages, tool_defs
             )
 
-        if MEMORY_GROUP in toolgroups and len(input_messages) > 0:
+        if RAG_TOOL_GROUP in toolgroups and len(input_messages) > 0:
             with tracing.span(MEMORY_QUERY_TOOL) as span:
                 step_id = str(uuid.uuid4())
                 yield AgentTurnResponseStreamChunk(
@@ -403,7 +403,7 @@ class ChatAgent(ShieldRunnerMixin):
                     )
                 )
 
-                args = toolgroup_args.get(MEMORY_GROUP, {})
+                args = toolgroup_args.get(RAG_TOOL_GROUP, {})
                 vector_db_ids = args.get("vector_db_ids", [])
                 query_config = args.get("query_config")
                 if query_config:
@@ -509,7 +509,7 @@ class ChatAgent(ShieldRunnerMixin):
                     tools=[
                         tool
                         for tool in tool_defs.values()
-                        if tool_to_group.get(tool.tool_name, None) != MEMORY_GROUP
+                        if tool_to_group.get(tool.tool_name, None) != RAG_TOOL_GROUP
                     ],
                     tool_prompt_format=self.agent_config.tool_prompt_format,
                     stream=True,
@@ -756,7 +756,7 @@ class ChatAgent(ShieldRunnerMixin):
             for tool_def in tools.data:
                 if (
                     toolgroup_name.startswith("builtin")
-                    and toolgroup_name != MEMORY_GROUP
+                    and toolgroup_name != RAG_TOOL_GROUP
                 ):
                     tool_name = tool_def.identifier
                     built_in_type = BuiltinTool.brave_search
diff --git a/llama_stack/providers/inline/agents/meta_reference/tests/test_chat_agent.py b/llama_stack/providers/inline/agents/meta_reference/tests/test_chat_agent.py
index 205868279..09fccd3c6 100644
--- a/llama_stack/providers/inline/agents/meta_reference/tests/test_chat_agent.py
+++ b/llama_stack/providers/inline/agents/meta_reference/tests/test_chat_agent.py
@@ -152,7 +152,7 @@ class MockToolGroupsAPI:
                     toolgroup_id=MEMORY_TOOLGROUP,
                     tool_host=ToolHost.client,
                     description="Mock tool",
-                    provider_id="builtin::memory",
+                    provider_id="builtin::rag",
                     parameters=[],
                 )
             ]
@@ -260,7 +260,7 @@ async def get_chat_agent(get_agents_impl):
     return await impl.get_agent(response.agent_id)
 
 
-MEMORY_TOOLGROUP = "builtin::memory"
+MEMORY_TOOLGROUP = "builtin::rag"
 CODE_INTERPRETER_TOOLGROUP = "builtin::code_interpreter"
 
 
diff --git a/llama_stack/providers/registry/tool_runtime.py b/llama_stack/providers/registry/tool_runtime.py
index 426fe22f2..927ca1886 100644
--- a/llama_stack/providers/registry/tool_runtime.py
+++ b/llama_stack/providers/registry/tool_runtime.py
@@ -19,7 +19,7 @@ def available_providers() -> List[ProviderSpec]:
     return [
         InlineProviderSpec(
             api=Api.tool_runtime,
-            provider_type="inline::memory-runtime",
+            provider_type="inline::rag-runtime",
             pip_packages=[],
             module="llama_stack.providers.inline.tool_runtime.memory",
             config_class="llama_stack.providers.inline.tool_runtime.memory.config.MemoryToolRuntimeConfig",
diff --git a/llama_stack/providers/tests/agents/test_agents.py b/llama_stack/providers/tests/agents/test_agents.py
index f11aef3ec..68ee9133c 100644
--- a/llama_stack/providers/tests/agents/test_agents.py
+++ b/llama_stack/providers/tests/agents/test_agents.py
@@ -184,7 +184,7 @@ class TestAgents:
         agent_config = AgentConfig(
             **{
                 **common_params,
-                "toolgroups": ["builtin::memory"],
+                "toolgroups": ["builtin::rag"],
                 "tool_choice": ToolChoice.auto,
             }
         )
diff --git a/llama_stack/providers/tests/tools/fixtures.py b/llama_stack/providers/tests/tools/fixtures.py
index 03752881a..a2dd4239a 100644
--- a/llama_stack/providers/tests/tools/fixtures.py
+++ b/llama_stack/providers/tests/tools/fixtures.py
@@ -22,8 +22,8 @@ def tool_runtime_memory_and_search() -> ProviderFixture:
     return ProviderFixture(
         providers=[
             Provider(
-                provider_id="memory-runtime",
-                provider_type="inline::memory-runtime",
+                provider_id="rag-runtime",
+                provider_type="inline::rag-runtime",
                 config={},
             ),
             Provider(
@@ -47,8 +47,8 @@ def tool_runtime_memory_and_search() -> ProviderFixture:
 @pytest.fixture(scope="session")
 def tool_group_input_memory() -> ToolGroupInput:
     return ToolGroupInput(
-        toolgroup_id="builtin::memory",
-        provider_id="memory-runtime",
+        toolgroup_id="builtin::rag",
+        provider_id="rag-runtime",
     )
 
 
diff --git a/llama_stack/templates/bedrock/bedrock.py b/llama_stack/templates/bedrock/bedrock.py
index 20f670891..6b83e9536 100644
--- a/llama_stack/templates/bedrock/bedrock.py
+++ b/llama_stack/templates/bedrock/bedrock.py
@@ -29,7 +29,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -58,8 +58,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/bedrock/build.yaml b/llama_stack/templates/bedrock/build.yaml
index 9ae11e9bb..6c07b0478 100644
--- a/llama_stack/templates/bedrock/build.yaml
+++ b/llama_stack/templates/bedrock/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/bedrock/run.yaml b/llama_stack/templates/bedrock/run.yaml
index 577263bbf..39408c1bd 100644
--- a/llama_stack/templates/bedrock/run.yaml
+++ b/llama_stack/templates/bedrock/run.yaml
@@ -78,8 +78,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -111,7 +111,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/cerebras/build.yaml b/llama_stack/templates/cerebras/build.yaml
index 6d43ed0ca..9d5ab1a52 100644
--- a/llama_stack/templates/cerebras/build.yaml
+++ b/llama_stack/templates/cerebras/build.yaml
@@ -27,5 +27,5 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
 image_type: conda
diff --git a/llama_stack/templates/cerebras/cerebras.py b/llama_stack/templates/cerebras/cerebras.py
index be51e635d..50a878645 100644
--- a/llama_stack/templates/cerebras/cerebras.py
+++ b/llama_stack/templates/cerebras/cerebras.py
@@ -33,7 +33,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
         ],
     }
 
@@ -79,8 +79,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/cerebras/run.yaml b/llama_stack/templates/cerebras/run.yaml
index 0553f0749..5a70890a8 100644
--- a/llama_stack/templates/cerebras/run.yaml
+++ b/llama_stack/templates/cerebras/run.yaml
@@ -83,8 +83,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
 metadata_store:
   type: sqlite
@@ -113,7 +113,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/fireworks/build.yaml b/llama_stack/templates/fireworks/build.yaml
index 7e19cd5e6..cdd60ec2a 100644
--- a/llama_stack/templates/fireworks/build.yaml
+++ b/llama_stack/templates/fireworks/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/fireworks/fireworks.py b/llama_stack/templates/fireworks/fireworks.py
index 5f1b9e8a0..546a8b82a 100644
--- a/llama_stack/templates/fireworks/fireworks.py
+++ b/llama_stack/templates/fireworks/fireworks.py
@@ -38,7 +38,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -86,8 +86,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/fireworks/run-with-safety.yaml b/llama_stack/templates/fireworks/run-with-safety.yaml
index 659ec5191..a4b425436 100644
--- a/llama_stack/templates/fireworks/run-with-safety.yaml
+++ b/llama_stack/templates/fireworks/run-with-safety.yaml
@@ -89,8 +89,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -168,7 +168,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/fireworks/run.yaml b/llama_stack/templates/fireworks/run.yaml
index 9fb61f842..a497317bd 100644
--- a/llama_stack/templates/fireworks/run.yaml
+++ b/llama_stack/templates/fireworks/run.yaml
@@ -83,8 +83,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -157,7 +157,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/hf-endpoint/build.yaml b/llama_stack/templates/hf-endpoint/build.yaml
index 82a460bd9..c2eaaa05b 100644
--- a/llama_stack/templates/hf-endpoint/build.yaml
+++ b/llama_stack/templates/hf-endpoint/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/hf-endpoint/hf_endpoint.py b/llama_stack/templates/hf-endpoint/hf_endpoint.py
index f9bfe85f9..4533fd95b 100644
--- a/llama_stack/templates/hf-endpoint/hf_endpoint.py
+++ b/llama_stack/templates/hf-endpoint/hf_endpoint.py
@@ -33,7 +33,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -76,8 +76,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/hf-endpoint/run-with-safety.yaml b/llama_stack/templates/hf-endpoint/run-with-safety.yaml
index dfa094fe6..0329f580b 100644
--- a/llama_stack/templates/hf-endpoint/run-with-safety.yaml
+++ b/llama_stack/templates/hf-endpoint/run-with-safety.yaml
@@ -88,8 +88,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -120,7 +120,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/hf-endpoint/run.yaml b/llama_stack/templates/hf-endpoint/run.yaml
index fb5d7fa31..8163fe28e 100644
--- a/llama_stack/templates/hf-endpoint/run.yaml
+++ b/llama_stack/templates/hf-endpoint/run.yaml
@@ -83,8 +83,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -110,7 +110,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/hf-serverless/build.yaml b/llama_stack/templates/hf-serverless/build.yaml
index 0eb4e0509..f9303cfab 100644
--- a/llama_stack/templates/hf-serverless/build.yaml
+++ b/llama_stack/templates/hf-serverless/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/hf-serverless/hf_serverless.py b/llama_stack/templates/hf-serverless/hf_serverless.py
index 4f3c29404..8438de7a5 100644
--- a/llama_stack/templates/hf-serverless/hf_serverless.py
+++ b/llama_stack/templates/hf-serverless/hf_serverless.py
@@ -33,7 +33,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -77,8 +77,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/hf-serverless/run-with-safety.yaml b/llama_stack/templates/hf-serverless/run-with-safety.yaml
index 0575efaef..9cee920a5 100644
--- a/llama_stack/templates/hf-serverless/run-with-safety.yaml
+++ b/llama_stack/templates/hf-serverless/run-with-safety.yaml
@@ -88,8 +88,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -120,7 +120,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/hf-serverless/run.yaml b/llama_stack/templates/hf-serverless/run.yaml
index b87edd744..c8ad0d38d 100644
--- a/llama_stack/templates/hf-serverless/run.yaml
+++ b/llama_stack/templates/hf-serverless/run.yaml
@@ -83,8 +83,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -110,7 +110,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/meta-reference-gpu/build.yaml b/llama_stack/templates/meta-reference-gpu/build.yaml
index f5371f0d6..b9130fc7d 100644
--- a/llama_stack/templates/meta-reference-gpu/build.yaml
+++ b/llama_stack/templates/meta-reference-gpu/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/meta-reference-gpu/meta_reference.py b/llama_stack/templates/meta-reference-gpu/meta_reference.py
index dae4f0218..a3f82b0c8 100644
--- a/llama_stack/templates/meta-reference-gpu/meta_reference.py
+++ b/llama_stack/templates/meta-reference-gpu/meta_reference.py
@@ -37,7 +37,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -83,8 +83,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml b/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
index 54ddef155..0faaabb15 100644
--- a/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
+++ b/llama_stack/templates/meta-reference-gpu/run-with-safety.yaml
@@ -90,8 +90,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -122,7 +122,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/meta-reference-gpu/run.yaml b/llama_stack/templates/meta-reference-gpu/run.yaml
index cde581d19..6ffe1fa36 100644
--- a/llama_stack/templates/meta-reference-gpu/run.yaml
+++ b/llama_stack/templates/meta-reference-gpu/run.yaml
@@ -84,8 +84,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -111,7 +111,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/meta-reference-quantized-gpu/build.yaml b/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
index aa23ad313..7bbcfe5f2 100644
--- a/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
+++ b/llama_stack/templates/meta-reference-quantized-gpu/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py b/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py
index 4e9cbf1fe..8c2a6ec9f 100644
--- a/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py
+++ b/llama_stack/templates/meta-reference-quantized-gpu/meta_reference.py
@@ -32,7 +32,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -42,8 +42,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/meta-reference-quantized-gpu/run.yaml b/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
index cc5793f8f..5ff87a901 100644
--- a/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
+++ b/llama_stack/templates/meta-reference-quantized-gpu/run.yaml
@@ -86,8 +86,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -113,7 +113,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/nvidia/build.yaml b/llama_stack/templates/nvidia/build.yaml
index d6a510e2e..e9748721a 100644
--- a/llama_stack/templates/nvidia/build.yaml
+++ b/llama_stack/templates/nvidia/build.yaml
@@ -25,6 +25,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/nvidia/nvidia.py b/llama_stack/templates/nvidia/nvidia.py
index 5693ba12d..19eb4bd5d 100644
--- a/llama_stack/templates/nvidia/nvidia.py
+++ b/llama_stack/templates/nvidia/nvidia.py
@@ -28,7 +28,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -56,8 +56,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/nvidia/run.yaml b/llama_stack/templates/nvidia/run.yaml
index 317aa1031..c57ca2b9a 100644
--- a/llama_stack/templates/nvidia/run.yaml
+++ b/llama_stack/templates/nvidia/run.yaml
@@ -80,8 +80,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -143,7 +143,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/ollama/build.yaml b/llama_stack/templates/ollama/build.yaml
index c3ed88fb8..0fee6808c 100644
--- a/llama_stack/templates/ollama/build.yaml
+++ b/llama_stack/templates/ollama/build.yaml
@@ -27,5 +27,5 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
 image_type: conda
diff --git a/llama_stack/templates/ollama/ollama.py b/llama_stack/templates/ollama/ollama.py
index bdbd1e142..d14cb3aad 100644
--- a/llama_stack/templates/ollama/ollama.py
+++ b/llama_stack/templates/ollama/ollama.py
@@ -35,7 +35,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
         ],
     }
     name = "ollama"
@@ -77,8 +77,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/ollama/run-with-safety.yaml b/llama_stack/templates/ollama/run-with-safety.yaml
index afb0b1938..5b5c9c253 100644
--- a/llama_stack/templates/ollama/run-with-safety.yaml
+++ b/llama_stack/templates/ollama/run-with-safety.yaml
@@ -85,8 +85,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
 metadata_store:
   type: sqlite
@@ -117,7 +117,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/ollama/run.yaml b/llama_stack/templates/ollama/run.yaml
index 976068670..3cc1cb2ac 100644
--- a/llama_stack/templates/ollama/run.yaml
+++ b/llama_stack/templates/ollama/run.yaml
@@ -82,8 +82,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
 metadata_store:
   type: sqlite
@@ -106,7 +106,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/remote-vllm/build.yaml b/llama_stack/templates/remote-vllm/build.yaml
index 409b2ba10..74d9f32d9 100644
--- a/llama_stack/templates/remote-vllm/build.yaml
+++ b/llama_stack/templates/remote-vllm/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/remote-vllm/run-with-safety.yaml b/llama_stack/templates/remote-vllm/run-with-safety.yaml
index e26d0f99f..4a0fa9a85 100644
--- a/llama_stack/templates/remote-vllm/run-with-safety.yaml
+++ b/llama_stack/templates/remote-vllm/run-with-safety.yaml
@@ -90,8 +90,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -122,7 +122,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/remote-vllm/run.yaml b/llama_stack/templates/remote-vllm/run.yaml
index dc54d216d..9631f94a2 100644
--- a/llama_stack/templates/remote-vllm/run.yaml
+++ b/llama_stack/templates/remote-vllm/run.yaml
@@ -84,8 +84,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -111,7 +111,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/remote-vllm/vllm.py b/llama_stack/templates/remote-vllm/vllm.py
index f91ad24a7..6c835ef86 100644
--- a/llama_stack/templates/remote-vllm/vllm.py
+++ b/llama_stack/templates/remote-vllm/vllm.py
@@ -35,7 +35,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -80,8 +80,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/tgi/build.yaml b/llama_stack/templates/tgi/build.yaml
index bc31ef7e7..8bc628158 100644
--- a/llama_stack/templates/tgi/build.yaml
+++ b/llama_stack/templates/tgi/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/tgi/run-with-safety.yaml b/llama_stack/templates/tgi/run-with-safety.yaml
index ea8057137..503505c32 100644
--- a/llama_stack/templates/tgi/run-with-safety.yaml
+++ b/llama_stack/templates/tgi/run-with-safety.yaml
@@ -83,8 +83,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -110,7 +110,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/tgi/run.yaml b/llama_stack/templates/tgi/run.yaml
index d537d0fce..f1953c513 100644
--- a/llama_stack/templates/tgi/run.yaml
+++ b/llama_stack/templates/tgi/run.yaml
@@ -82,8 +82,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -109,7 +109,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/tgi/tgi.py b/llama_stack/templates/tgi/tgi.py
index 230fcac2a..e49c98d72 100644
--- a/llama_stack/templates/tgi/tgi.py
+++ b/llama_stack/templates/tgi/tgi.py
@@ -35,7 +35,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -80,8 +80,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/together/build.yaml b/llama_stack/templates/together/build.yaml
index 2160adb8e..90ee5bcee 100644
--- a/llama_stack/templates/together/build.yaml
+++ b/llama_stack/templates/together/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/together/run-with-safety.yaml b/llama_stack/templates/together/run-with-safety.yaml
index 54b918eea..ec351108e 100644
--- a/llama_stack/templates/together/run-with-safety.yaml
+++ b/llama_stack/templates/together/run-with-safety.yaml
@@ -89,8 +89,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -163,7 +163,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/together/run.yaml b/llama_stack/templates/together/run.yaml
index 2c0475796..c2afd98e9 100644
--- a/llama_stack/templates/together/run.yaml
+++ b/llama_stack/templates/together/run.yaml
@@ -83,8 +83,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -152,7 +152,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/together/together.py b/llama_stack/templates/together/together.py
index ec64527d2..5e9520433 100644
--- a/llama_stack/templates/together/together.py
+++ b/llama_stack/templates/together/together.py
@@ -38,7 +38,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -76,8 +76,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/llama_stack/templates/vllm-gpu/build.yaml b/llama_stack/templates/vllm-gpu/build.yaml
index 45f543071..d24046613 100644
--- a/llama_stack/templates/vllm-gpu/build.yaml
+++ b/llama_stack/templates/vllm-gpu/build.yaml
@@ -27,6 +27,6 @@ distribution_spec:
     - remote::brave-search
     - remote::tavily-search
     - inline::code-interpreter
-    - inline::memory-runtime
+    - inline::rag-runtime
     - remote::model-context-protocol
 image_type: conda
diff --git a/llama_stack/templates/vllm-gpu/run.yaml b/llama_stack/templates/vllm-gpu/run.yaml
index 2d9ec6a3f..165e4d51d 100644
--- a/llama_stack/templates/vllm-gpu/run.yaml
+++ b/llama_stack/templates/vllm-gpu/run.yaml
@@ -86,8 +86,8 @@ providers:
   - provider_id: code-interpreter
     provider_type: inline::code-interpreter
     config: {}
-  - provider_id: memory-runtime
-    provider_type: inline::memory-runtime
+  - provider_id: rag-runtime
+    provider_type: inline::rag-runtime
     config: {}
   - provider_id: model-context-protocol
     provider_type: remote::model-context-protocol
@@ -113,7 +113,7 @@ eval_tasks: []
 tool_groups:
 - toolgroup_id: builtin::websearch
   provider_id: tavily-search
-- toolgroup_id: builtin::memory
-  provider_id: memory-runtime
+- toolgroup_id: builtin::rag
+  provider_id: rag-runtime
 - toolgroup_id: builtin::code_interpreter
   provider_id: code-interpreter
diff --git a/llama_stack/templates/vllm-gpu/vllm.py b/llama_stack/templates/vllm-gpu/vllm.py
index a8f13ce40..54ebd2d41 100644
--- a/llama_stack/templates/vllm-gpu/vllm.py
+++ b/llama_stack/templates/vllm-gpu/vllm.py
@@ -32,7 +32,7 @@ def get_distribution_template() -> DistributionTemplate:
             "remote::brave-search",
             "remote::tavily-search",
             "inline::code-interpreter",
-            "inline::memory-runtime",
+            "inline::rag-runtime",
             "remote::model-context-protocol",
         ],
     }
@@ -72,8 +72,8 @@ def get_distribution_template() -> DistributionTemplate:
             provider_id="tavily-search",
         ),
         ToolGroupInput(
-            toolgroup_id="builtin::memory",
-            provider_id="memory-runtime",
+            toolgroup_id="builtin::rag",
+            provider_id="rag-runtime",
         ),
         ToolGroupInput(
             toolgroup_id="builtin::code_interpreter",
diff --git a/tests/client-sdk/agents/test_agents.py b/tests/client-sdk/agents/test_agents.py
index 7c13f5768..c6be91232 100644
--- a/tests/client-sdk/agents/test_agents.py
+++ b/tests/client-sdk/agents/test_agents.py
@@ -292,7 +292,7 @@ def test_rag_agent(llama_stack_client, agent_config):
         **agent_config,
         "toolgroups": [
             dict(
-                name="builtin::memory",
+                name="builtin::rag",
                 args={
                     "vector_db_ids": [vector_db_id],
                 },

From 0bff6e1658826c181b3b75003807296a5f236304 Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 20:25:02 -0800
Subject: [PATCH 091/107] Move tool_runtime.memory -> tool_runtime.rag

---
 .../providers/inline/tool_runtime/{memory => rag}/__init__.py | 4 ++--
 .../providers/inline/tool_runtime/{memory => rag}/config.py   | 2 +-
 .../inline/tool_runtime/{memory => rag}/context_retriever.py  | 0
 .../providers/inline/tool_runtime/{memory => rag}/memory.py   | 4 ++--
 llama_stack/providers/registry/tool_runtime.py                | 4 ++--
 5 files changed, 7 insertions(+), 7 deletions(-)
 rename llama_stack/providers/inline/tool_runtime/{memory => rag}/__init__.py (77%)
 rename llama_stack/providers/inline/tool_runtime/{memory => rag}/config.py (85%)
 rename llama_stack/providers/inline/tool_runtime/{memory => rag}/context_retriever.py (100%)
 rename llama_stack/providers/inline/tool_runtime/{memory => rag}/memory.py (98%)

diff --git a/llama_stack/providers/inline/tool_runtime/memory/__init__.py b/llama_stack/providers/inline/tool_runtime/rag/__init__.py
similarity index 77%
rename from llama_stack/providers/inline/tool_runtime/memory/__init__.py
rename to llama_stack/providers/inline/tool_runtime/rag/__init__.py
index 42a0a6b01..542872091 100644
--- a/llama_stack/providers/inline/tool_runtime/memory/__init__.py
+++ b/llama_stack/providers/inline/tool_runtime/rag/__init__.py
@@ -8,11 +8,11 @@ from typing import Any, Dict
 
 from llama_stack.providers.datatypes import Api
 
-from .config import MemoryToolRuntimeConfig
+from .config import RagToolRuntimeConfig
 from .memory import MemoryToolRuntimeImpl
 
 
-async def get_provider_impl(config: MemoryToolRuntimeConfig, deps: Dict[str, Any]):
+async def get_provider_impl(config: RagToolRuntimeConfig, deps: Dict[str, Any]):
     impl = MemoryToolRuntimeImpl(config, deps[Api.vector_io], deps[Api.inference])
     await impl.initialize()
     return impl
diff --git a/llama_stack/providers/inline/tool_runtime/memory/config.py b/llama_stack/providers/inline/tool_runtime/rag/config.py
similarity index 85%
rename from llama_stack/providers/inline/tool_runtime/memory/config.py
rename to llama_stack/providers/inline/tool_runtime/rag/config.py
index 4a20c986c..2d0d2f595 100644
--- a/llama_stack/providers/inline/tool_runtime/memory/config.py
+++ b/llama_stack/providers/inline/tool_runtime/rag/config.py
@@ -7,5 +7,5 @@
 from pydantic import BaseModel
 
 
-class MemoryToolRuntimeConfig(BaseModel):
+class RagToolRuntimeConfig(BaseModel):
     pass
diff --git a/llama_stack/providers/inline/tool_runtime/memory/context_retriever.py b/llama_stack/providers/inline/tool_runtime/rag/context_retriever.py
similarity index 100%
rename from llama_stack/providers/inline/tool_runtime/memory/context_retriever.py
rename to llama_stack/providers/inline/tool_runtime/rag/context_retriever.py
diff --git a/llama_stack/providers/inline/tool_runtime/memory/memory.py b/llama_stack/providers/inline/tool_runtime/rag/memory.py
similarity index 98%
rename from llama_stack/providers/inline/tool_runtime/memory/memory.py
rename to llama_stack/providers/inline/tool_runtime/rag/memory.py
index 7798ed711..9a2687925 100644
--- a/llama_stack/providers/inline/tool_runtime/memory/memory.py
+++ b/llama_stack/providers/inline/tool_runtime/rag/memory.py
@@ -32,7 +32,7 @@ from llama_stack.providers.utils.memory.vector_store import (
     make_overlapped_chunks,
 )
 
-from .config import MemoryToolRuntimeConfig
+from .config import RagToolRuntimeConfig
 from .context_retriever import generate_rag_query
 
 log = logging.getLogger(__name__)
@@ -47,7 +47,7 @@ def make_random_string(length: int = 8):
 class MemoryToolRuntimeImpl(ToolsProtocolPrivate, ToolRuntime, RAGToolRuntime):
     def __init__(
         self,
-        config: MemoryToolRuntimeConfig,
+        config: RagToolRuntimeConfig,
         vector_io_api: VectorIO,
         inference_api: Inference,
     ):
diff --git a/llama_stack/providers/registry/tool_runtime.py b/llama_stack/providers/registry/tool_runtime.py
index 927ca1886..33d880f30 100644
--- a/llama_stack/providers/registry/tool_runtime.py
+++ b/llama_stack/providers/registry/tool_runtime.py
@@ -21,8 +21,8 @@ def available_providers() -> List[ProviderSpec]:
             api=Api.tool_runtime,
             provider_type="inline::rag-runtime",
             pip_packages=[],
-            module="llama_stack.providers.inline.tool_runtime.memory",
-            config_class="llama_stack.providers.inline.tool_runtime.memory.config.MemoryToolRuntimeConfig",
+            module="llama_stack.providers.inline.tool_runtime.rag",
+            config_class="llama_stack.providers.inline.tool_runtime.rag.config.RagToolRuntimeConfig",
             api_dependencies=[Api.vector_io, Api.inference],
         ),
         InlineProviderSpec(

From 6c205e1d5a2ba38e3773bb4cab4bcc5aa43f5dcb Mon Sep 17 00:00:00 2001
From: Ashwin Bharambe 
Date: Wed, 22 Jan 2025 20:31:18 -0800
Subject: [PATCH 092/107] Fix tool tests

---
 llama_stack/providers/tests/tools/conftest.py   | 7 +++----
 llama_stack/providers/tests/tools/test_tools.py | 2 +-
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/llama_stack/providers/tests/tools/conftest.py b/llama_stack/providers/tests/tools/conftest.py
index 525abe8ab..0df547a9d 100644
--- a/llama_stack/providers/tests/tools/conftest.py
+++ b/llama_stack/providers/tests/tools/conftest.py
@@ -8,8 +8,8 @@ import pytest
 
 from ..conftest import get_provider_fixture_overrides
 from ..inference.fixtures import INFERENCE_FIXTURES
-from ..memory.fixtures import MEMORY_FIXTURES
 from ..safety.fixtures import SAFETY_FIXTURES
+from ..vector_io.fixtures import VECTOR_IO_FIXTURES
 from .fixtures import TOOL_RUNTIME_FIXTURES
 
 DEFAULT_PROVIDER_COMBINATIONS = [
@@ -17,7 +17,7 @@ DEFAULT_PROVIDER_COMBINATIONS = [
         {
             "inference": "together",
             "safety": "llama_guard",
-            "memory": "faiss",
+            "vector_io": "faiss",
             "tool_runtime": "memory_and_search",
         },
         id="together",
@@ -39,12 +39,11 @@ def pytest_generate_tests(metafunc):
         available_fixtures = {
             "inference": INFERENCE_FIXTURES,
             "safety": SAFETY_FIXTURES,
-            "memory": MEMORY_FIXTURES,
+            "vector_io": VECTOR_IO_FIXTURES,
             "tool_runtime": TOOL_RUNTIME_FIXTURES,
         }
         combinations = (
             get_provider_fixture_overrides(metafunc.config, available_fixtures)
             or DEFAULT_PROVIDER_COMBINATIONS
         )
-        print(combinations)
         metafunc.parametrize("tools_stack", combinations, indirect=True)
diff --git a/llama_stack/providers/tests/tools/test_tools.py b/llama_stack/providers/tests/tools/test_tools.py
index bb4265f94..281ea404d 100644
--- a/llama_stack/providers/tests/tools/test_tools.py
+++ b/llama_stack/providers/tests/tools/test_tools.py
@@ -88,7 +88,7 @@ class TestTools:
         tools_impl = tools_stack.impls[Api.tool_runtime]
 
         # Register memory bank
-        await vector_dbs_impl.register(
+        await vector_dbs_impl.register_vector_db(
             vector_db_id="test_bank",
             embedding_model="all-MiniLM-L6-v2",
             embedding_dimension=384,

From 65f07c3d6345984c1c49a317eac0575d02406b67 Mon Sep 17 00:00:00 2001
From: Hardik Shah 
Date: Wed, 22 Jan 2025 20:38:52 -0800
Subject: [PATCH 093/107] Update Documentation (#838)

# What does this PR do?

Update README and other documentation


## Before submitting

- [X] This PR fixes a typo or improves the docs (you can dismiss the
other checks if that's the case).
- [ ] Ran pre-commit to handle lint / formatting issues.
- [ ] Read the [contributor
guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md),
      Pull Request section?
- [ ] Updated relevant documentation.
- [ ] Wrote necessary unit or integration tests.
---
 README.md                            |  86 ++++++-----------
 docs/source/concepts/index.md        |   7 +-
 docs/source/getting_started/index.md | 139 +++++++++++++++++----------
 docs/source/index.md                 |  13 ++-
 docs/source/introduction/index.md    |  84 +++++-----------
 5 files changed, 146 insertions(+), 183 deletions(-)

diff --git a/README.md b/README.md
index 61a0f33fe..b1878d7e4 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,11 @@
 [![PyPI - Downloads](https://img.shields.io/pypi/dm/llama-stack)](https://pypi.org/project/llama-stack/)
 [![Discord](https://img.shields.io/discord/1257833999603335178)](https://discord.gg/llama-stack)
 
-[**Quick Start**](https://llama-stack.readthedocs.io/en/latest/getting_started/index.html) | [**Documentation**](https://llama-stack.readthedocs.io/en/latest/index.html) | [**Zero-to-Hero Guide**](https://github.com/meta-llama/llama-stack/tree/main/docs/zero_to_hero_guide)
+[**Quick Start**](https://llama-stack.readthedocs.io/en/latest/getting_started/index.html) | [**Documentation**](https://llama-stack.readthedocs.io/en/latest/index.html) | [**Colab Notebook**](./docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb)
 
-Llama Stack defines and standardizes the set of core building blocks needed to bring generative AI applications to market. These building blocks are presented in the form of interoperable APIs with a broad set of Service Providers providing their implementations.
+Llama Stack defines and standardizes the core building blocks needed to bring generative AI applications to market. It provides a unified set of APIs with implementations from leading service providers, enabling seamless transitions between development and production environments.
+
+We focus on making it easy to build production applications with the Llama model family - from the latest Llama 3.3 to specialized models like Llama Guard for safety.
 
 
-Our goal is to provide pre-packaged implementations which can be operated in a variety of deployment environments: developers start iterating with Desktops or their mobile devices and can seamlessly transition to on-prem or public cloud deployments. At every point in this transition, the same set of APIs and the same developer experience is available. +## Key Features -> ⚠️ **Note** -> The Stack APIs are rapidly improving, but still very much work in progress and we invite feedback as well as direct contributions. +- **Unified API Layer** for: + - Inference: Run LLM models efficiently + - Safety: Apply content filtering and safety policies + - DatasetIO: Store and retrieve knowledge for RAG + - Agents: Build multi-step agentic workflows + - Evaluation: Test and improve model and agent quality + - Telemetry: Collect and analyze usage data and complex agentic traces + - Post Training ( Coming Soon ): Fine tune models for specific use cases +- **Rich Provider Ecosystem** + - Local Development: Meta's Reference,Ollama, vLLM, TGI + - Self-hosted: Chroma, pgvector, Nvidia NIM + - Cloud: Fireworks, Together, Nvidia, AWS Bedrock, Groq, Cerebras + - On-device: iOS and Android support -## APIs - -We have working implementations of the following APIs today: -- Inference -- Safety -- Memory -- Agents -- Eval -- Telemetry - -Alongside these APIs, we also related APIs for operating with associated resources (see [Concepts](https://llama-stack.readthedocs.io/en/latest/concepts/index.html#resources)): - -- Models -- Shields -- Memory Banks -- Eval Tasks -- Datasets -- Scoring Functions - -We are also working on the following APIs which will be released soon: - -- Post Training -- Synthetic Data Generation -- Reward Scoring - -Each of the APIs themselves is a collection of REST endpoints. - -## Philosophy - -### Service-oriented design - -Unlike other frameworks, Llama Stack is built with a service-oriented, REST API-first approach. Such a design not only allows for seamless transitions from a local to remote deployments, but also forces the design to be more declarative. We believe this restriction can result in a much simpler, robust developer experience. This will necessarily trade-off against expressivity however if we get the APIs right, it can lead to a very powerful platform. - -### Composability - -We expect the set of APIs we design to be composable. An Agent abstractly depends on { Inference, Memory, Safety } APIs but does not care about the actual implementation details. Safety itself may require model inference and hence can depend on the Inference API. - -### Turnkey one-stop solutions - -We expect to provide turnkey solutions for popular deployment scenarios. It should be easy to deploy a Llama Stack server on AWS or on a private data center. Either of these should allow a developer to get started with powerful agentic apps, model evaluations or fine-tuning services in a matter of minutes. They should all result in the same uniform observability and developer experience. - -### Focus on Llama models - -As a Meta initiated project, we have started by explicitly focusing on Meta's Llama series of models. Supporting the broad set of open models is no easy task and we want to start with models we understand best. - -### Supporting the Ecosystem - -There is a vibrant ecosystem of Providers which provide efficient inference or scalable vector stores or powerful observability solutions. We want to make sure it is easy for developers to pick and choose the best implementations for their use cases. We also want to make sure it is easy for new Providers to onboard and participate in the ecosystem. - -Additionally, we have designed every element of the Stack such that APIs as well as Resources (like Models) can be federated. +- **Built for Production** + - Pre-packaged distributions for common deployment scenarios + - Comprehensive evaluation capabilities + - Full observability and monitoring + - Provider federation and fallback ## Supported Llama Stack Implementations @@ -87,14 +55,16 @@ Additionally, we have designed every element of the Stack such that APIs as well | Groq | Hosted | | :heavy_check_mark: | | | | | Ollama | Single Node | | :heavy_check_mark: | | | | | TGI | Hosted and Single Node | | :heavy_check_mark: | | | | -| [NVIDIA NIM](https://build.nvidia.com/nim?filters=nimType%3Anim_type_run_anywhere&q=llama) | Hosted and Single Node | | :heavy_check_mark: | | | | +| NVIDIA NIM | Hosted and Single Node | | :heavy_check_mark: | | | | | Chroma | Single Node | | | :heavy_check_mark: | | | | PG Vector | Single Node | | | :heavy_check_mark: | | | | PyTorch ExecuTorch | On-device iOS | :heavy_check_mark: | :heavy_check_mark: | | | | -| [vLLM](https://github.com/vllm-project/vllm) | Hosted and Single Node | | :heavy_check_mark: | | | | +| vLLM | Hosted and Single Node | | :heavy_check_mark: | | | | ### Distributions +A Llama Stack Distribution (or "distro") is a pre-configured bundle of provider implementations for each API component. Distributions make it easy to get started with a specific deployment scenario - you can begin with a local development setup (eg. ollama) and seamlessly transition to production (eg. Fireworks) without changing your application code. Here are some of the distributions we support: + | **Distribution** | **Llama Stack Docker** | Start This Distribution | |:---------------------------------------------:|:-------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------:| | Meta Reference | [llamastack/distribution-meta-reference-gpu](https://hub.docker.com/repository/docker/llamastack/distribution-meta-reference-gpu/general) | [Guide](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/meta-reference-gpu.html) | @@ -104,7 +74,7 @@ Additionally, we have designed every element of the Stack such that APIs as well | TGI | [llamastack/distribution-tgi](https://hub.docker.com/repository/docker/llamastack/distribution-tgi/general) | [Guide](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/tgi.html) | | Together | [llamastack/distribution-together](https://hub.docker.com/repository/docker/llamastack/distribution-together/general) | [Guide](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/together.html) | | Fireworks | [llamastack/distribution-fireworks](https://hub.docker.com/repository/docker/llamastack/distribution-fireworks/general) | [Guide](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/fireworks.html) | -| [vLLM](https://github.com/vllm-project/vllm) | [llamastack/distribution-remote-vllm](https://hub.docker.com/repository/docker/llamastack/distribution-remote-vllm/general) | [Guide](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/remote-vllm.html) | +| vLLM | [llamastack/distribution-remote-vllm](https://hub.docker.com/repository/docker/llamastack/distribution-remote-vllm/general) | [Guide](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/remote-vllm.html) | ## Installation diff --git a/docs/source/concepts/index.md b/docs/source/concepts/index.md index 32caa66a5..02e54d839 100644 --- a/docs/source/concepts/index.md +++ b/docs/source/concepts/index.md @@ -10,7 +10,6 @@ A Llama Stack API is described as a collection of REST endpoints. We currently s - **Inference**: run inference with a LLM - **Safety**: apply safety policies to the output at a Systems (not only model) level - **Agents**: run multi-step agentic workflows with LLMs with tool usage, memory (RAG), etc. -- **Memory**: store and retrieve data for RAG, chat history, etc. - **DatasetIO**: interface with datasets and data loaders - **Scoring**: evaluate outputs of the system - **Eval**: generate outputs (via Inference or Agents) and perform scoring @@ -39,7 +38,6 @@ Some of these APIs are associated with a set of **Resources**. Here is the mappi - **Inference**, **Eval** and **Post Training** are associated with `Model` resources. - **Safety** is associated with `Shield` resources. -- **Memory** is associated with `Memory Bank` resources. - **DatasetIO** is associated with `Dataset` resources. - **Scoring** is associated with `ScoringFunction` resources. - **Eval** is associated with `Model` and `EvalTask` resources. @@ -63,12 +61,9 @@ While there is a lot of flexibility to mix-and-match providers, often users will **On-device Distro**: Finally, you may want to run Llama Stack directly on an edge device (mobile phone or a tablet.) We provide Distros for iOS and Android (coming soon.) -## More Concepts -- [Evaluation Concepts](evaluation_concepts.md) - ```{toctree} :maxdepth: 1 :hidden: -evaluation_concepts +distributions/index ``` diff --git a/docs/source/getting_started/index.md b/docs/source/getting_started/index.md index 602b5a635..aba3de54e 100644 --- a/docs/source/getting_started/index.md +++ b/docs/source/getting_started/index.md @@ -1,26 +1,24 @@ # Quick Start -In this guide, we'll walk through how you can use the Llama Stack client SDK to build a simple RAG agent. +In this guide, we'll walk through how you can use the Llama Stack (server and client SDK ) to test a simple RAG agent. -The most critical requirement for running the agent is running inference on the underlying Llama model. Depending on what hardware (GPUs) you have available, you have various options. We will use `Ollama` for this purpose as it is the easiest to get started with and yet robust. +A Llama Stack agent is a simple autonomous system that can perform tasks by combining a Llama model for reasoning with tools (e.g., RAG, web search, code execution, etc.) for taking actions. -First, let's set up some environment variables that we will use in the rest of the guide. Note that if you open up a new terminal, you will need to set these again. +At minimum, an agent requires a Llama model for inference and at least one tool that it can use. + +In Llama Stack, we provide a server exposing multiple APIs. These APIs are backed by implementations from different providers. For this guide, we will use [Ollama](https://ollama.com/) as the inference provider. -```bash -export INFERENCE_MODEL="meta-llama/Llama-3.2-3B-Instruct" -# ollama names this model differently, and we must use the ollama name when loading the model -export OLLAMA_INFERENCE_MODEL="llama3.2:3b-instruct-fp16" -export LLAMA_STACK_PORT=5001 -``` ### 1. Start Ollama ```bash -ollama run $OLLAMA_INFERENCE_MODEL --keepalive 60m +ollama run llama3.2:3b-instruct-fp16 --keepalive 60m ``` By default, Ollama keeps the model loaded in memory for 5 minutes which can be too short. We set the `--keepalive` flag to 60 minutes to ensure the model remains loaded for sometime. +NOTE: If you do not have ollama, you can install it from [here](https://ollama.ai/docs/installation). + ### 2. Start the Llama Stack server @@ -28,6 +26,13 @@ Llama Stack is based on a client-server architecture. It consists of a server wh To get started quickly, we provide various Docker images for the server component that work with different inference providers out of the box. For this guide, we will use `llamastack/distribution-ollama` as the Docker image. +Lets setup some environment variables that we will use in the rest of the guide. +```bash +INFERENCE_MODEL="meta-llama/Llama-3.2-3B-Instruct" +LLAMA_STACK_PORT=8321 +``` + +You can start the server using the following command: ```bash docker run -it \ -p $LLAMA_STACK_PORT:$LLAMA_STACK_PORT \ @@ -45,6 +50,9 @@ Configuration for this is available at `distributions/ollama/run.yaml`. You can interact with the Llama Stack server using various client SDKs. We will use the Python SDK which you can install using the following command. Note that you must be using Python 3.10 or newer: ```bash +yes | conda create -n stack-client python=3.10 +conda activate stack-client + pip install llama-stack-client ``` @@ -76,7 +84,10 @@ client = LlamaStackClient(base_url=f"http://localhost:{os.environ['LLAMA_STACK_P # List available models models = client.models.list() -print(models) +print("--- Available models: ---") +for m in models: + print(f"- {m.identifier}") +print() response = client.inference.chat_completion( model_id=os.environ["INFERENCE_MODEL"], @@ -93,59 +104,83 @@ print(response.completion_message.content) Here is an example of a simple RAG agent that uses the Llama Stack client SDK. ```python -import asyncio import os +from termcolor import cprint -from llama_stack_client import LlamaStackClient from llama_stack_client.lib.agents.agent import Agent from llama_stack_client.lib.agents.event_logger import EventLogger -from llama_stack_client.types import Attachment from llama_stack_client.types.agent_create_params import AgentConfig +from llama_stack_client.types.tool_runtime import DocumentParam as Document +from llama_stack_client import LlamaStackClient -async def run_main(): - urls = ["chat.rst", "llama3.rst", "datasets.rst", "lora_finetune.rst"] - attachments = [ - Attachment( - content=f"https://raw.githubusercontent.com/pytorch/torchtune/main/docs/source/tutorials/{url}", - mime_type="text/plain", - ) - for i, url in enumerate(urls) - ] +# Define the client and point it to the server URL +client = LlamaStackClient(base_url=f"http://localhost:{os.environ['LLAMA_STACK_PORT']}") - client = LlamaStackClient(base_url=f"http://localhost:{os.environ['LLAMA_STACK_PORT']}") - - agent_config = AgentConfig( - model=os.environ["INFERENCE_MODEL"], - instructions="You are a helpful assistant", - tools=[{"type": "memory"}], # enable Memory aka RAG - enable_session_persistence=True, +# Define the documents to be used for RAG +urls = ["chat.rst", "llama3.rst", "datasets.rst", "lora_finetune.rst"] +documents = [ + Document( + document_id=f"num-{i}", + content=f"https://raw.githubusercontent.com/pytorch/torchtune/main/docs/source/tutorials/{url}", + mime_type="text/plain", + metadata={}, ) + for i, url in enumerate(urls) +] - agent = Agent(client, agent_config) - session_id = agent.create_session("test-session") - user_prompts = [ - ( - "I am attaching documentation for Torchtune. Help me answer questions I will ask next.", - attachments, - ), - ( - "What are the top 5 topics that were explained? Only list succinct bullet points.", - None, - ), - ] - for prompt, attachments in user_prompts: - response = agent.create_turn( - messages=[{"role": "user", "content": prompt}], - attachments=attachments, - session_id=session_id, - ) - for log in EventLogger().log(response): - log.print() +# Register a vector database +vector_db_id = "test-vector-db" +client.vector_dbs.register( + vector_db_id=vector_db_id, + embedding_model="all-MiniLM-L6-v2", + embedding_dimension=384, +) +# Insert the documents into the vector database +client.tool_runtime.rag_tool.insert( + documents=documents, + vector_db_id=vector_db_id, + chunk_size_in_tokens=512, +) -if __name__ == "__main__": - asyncio.run(run_main()) +# Create an agent +agent_config = AgentConfig( + # Define the inference model to use + model=os.environ["INFERENCE_MODEL"], + # Define instructions for the agent ( aka system prompt) + instructions="You are a helpful assistant", + # Enable session persistence + enable_session_persistence=False, + # Define tools available to the agent + toolgroups = [ + { + "name": "builtin::memory", + "args" : { + "vector_db_ids": [vector_db_id], + } + } + ], +) + +# Create an agent session +rag_agent = Agent(client, agent_config) +session_id = rag_agent.create_session("test-session") + +# Define a user prompts +user_prompts = [ + "What are the top 5 topics that were explained? Only list succinct bullet points.", +] + +# Run the agent loop by calling the `create_turn` method +for prompt in user_prompts: + cprint(f'User> {prompt}', 'green') + response = rag_agent.create_turn( + messages=[{"role": "user", "content": prompt}], + session_id=session_id, + ) + for log in EventLogger().log(response): + log.print() ``` ## Next Steps diff --git a/docs/source/index.md b/docs/source/index.md index cf7c0b236..7e7977738 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -1,17 +1,15 @@ # Llama Stack -Llama Stack defines and standardizes the set of core building blocks needed to bring generative AI applications to market. These building blocks are presented in the form of interoperable APIs with a broad set of Service Providers providing their implementations. +Llama Stack defines and standardizes the core building blocks needed to bring generative AI applications to market. It provides a unified set of APIs with implementations from leading service providers, enabling seamless transitions between development and production environments. + +We focus on making it easy to build production applications with the Llama model family - from the latest Llama 3.3 to specialized models like Llama Guard for safety. ```{image} ../_static/llama-stack.png :alt: Llama Stack :width: 400px ``` -Our goal is to provide pre-packaged implementations which can be operated in a variety of deployment environments: developers start iterating with Desktops or their mobile devices and can seamlessly transition to on-prem or public cloud deployments. At every point in this transition, the same set of APIs and the same developer experience is available. - -```{note} -The Stack APIs are rapidly improving but still a work-in-progress. We invite feedback as well as direct contributions. -``` +Our goal is to provide pre-packaged implementations (aka "distributions") which can be run in a variety of deployment environments. LlamaStack can assist you in your entire app development lifecycle - start iterating on local, mobile or desktop and seamlessly transition to on-prem or public cloud deployments. At every point in this transition, the same set of APIs and the same developer experience is available. ## Quick Links @@ -44,7 +42,7 @@ A number of "adapters" are available for some popular Inference and Memory (Vect | Together | Hosted | Y | Y | | Y | | | Ollama | Single Node | | Y | | | | TGI | Hosted and Single Node | | Y | | | -| [NVIDIA NIM](https://build.nvidia.com/nim?filters=nimType%3Anim_type_run_anywhere&q=llama) | Hosted and Single Node | | Y | | | +| NVIDIA NIM | Hosted and Single Node | | Y | | | | Chroma | Single Node | | | Y | | | | Postgres | Single Node | | | Y | | | | PyTorch ExecuTorch | On-device iOS | Y | Y | | | @@ -54,6 +52,7 @@ A number of "adapters" are available for some popular Inference and Memory (Vect :hidden: :maxdepth: 3 +self introduction/index getting_started/index concepts/index diff --git a/docs/source/introduction/index.md b/docs/source/introduction/index.md index 9c2a70341..beae53158 100644 --- a/docs/source/introduction/index.md +++ b/docs/source/introduction/index.md @@ -19,77 +19,41 @@ Building production AI applications today requires solving multiple challenges: - Changing providers requires significant code changes. -### The Vision: A Universal Stack - +### Our Solution: A Universal Stack ```{image} ../../_static/llama-stack.png :alt: Llama Stack :width: 400px ``` -Llama Stack defines and standardizes the core building blocks needed to bring generative AI applications to market. These building blocks are presented as interoperable APIs with a broad set of Service Providers providing their implementations. +Llama Stack addresses these challenges through a service-oriented, API-first approach: -#### Service-oriented Design -Unlike other frameworks, Llama Stack is built with a service-oriented, REST API-first approach. Such a design not only allows for seamless transitions from local to remote deployments but also forces the design to be more declarative. This restriction can result in a much simpler, robust developer experience. The same code works across different environments: +**Develop Anywhere, Deploy Everywhere** +- Start locally with CPU-only setups +- Move to GPU acceleration when needed +- Deploy to cloud or edge without code changes +- Same APIs and developer experience everywhere -- Local development with CPU-only setups -- Self-hosted with GPU acceleration -- Cloud-hosted on providers like AWS, Fireworks, Together -- On-device for iOS and Android - - -#### Composability -The APIs we design are composable. An Agent abstractly depends on { Inference, Memory, Safety } APIs but does not care about the actual implementation details. Safety itself may require model inference and hence can depend on the Inference API. - -#### Turnkey Solutions - -We provide turnkey solutions for popular deployment scenarios. It should be easy to deploy a Llama Stack server on AWS or in a private data center. Either of these should allow a developer to get started with powerful agentic apps, model evaluations, or fine-tuning services in minutes. - -We have built-in support for critical needs: - -- Safety guardrails and content filtering -- Comprehensive evaluation capabilities +**Production-Ready Building Blocks** +- Pre-built safety guardrails and content filtering +- Built-in RAG and agent capabilities +- Comprehensive evaluation toolkit - Full observability and monitoring -- Provider federation and fallback -#### Focus on Llama Models -As a Meta-initiated project, we explicitly focus on Meta's Llama series of models. Supporting the broad set of open models is no easy task and we want to start with models we understand best. - -#### Supporting the Ecosystem -There is a vibrant ecosystem of Providers which provide efficient inference or scalable vector stores or powerful observability solutions. We want to make sure it is easy for developers to pick and choose the best implementations for their use cases. We also want to make sure it is easy for new Providers to onboard and participate in the ecosystem. - -Additionally, we have designed every element of the Stack such that APIs as well as Resources (like Models) can be federated. - -#### Rich Provider Ecosystem - -```{list-table} -:header-rows: 1 - -* - Provider - - Local - - Self-hosted - - Cloud -* - Inference - - Ollama - - vLLM, TGI - - Fireworks, Together, AWS -* - Memory - - FAISS - - Chroma, pgvector - - Weaviate -* - Safety - - Llama Guard - - - - - AWS Bedrock -``` +**True Provider Independence** +- Swap providers without application changes +- Mix and match best-in-class implementations +- Federation and fallback support +- No vendor lock-in -### Unified API Layer +### Our Philosophy -Llama Stack provides a consistent interface for: +- **Service-Oriented**: REST APIs enforce clean interfaces and enable seamless transitions across different environments. +- **Composability**: Every component is independent but works together seamlessly +- **Production Ready**: Built for real-world applications, not just demos +- **Turnkey Solutions**: Easy to deploy built in solutions for popular deployment scenarios +- **Llama First**: Explicit focus on Meta's Llama models and partnering ecosystem -- **Inference**: Run LLM models efficiently -- **Safety**: Apply content filtering and safety policies -- **Memory**: Store and retrieve knowledge for RAG -- **Agents**: Build multi-step workflows -- **Evaluation**: Test and improve application quality + +With Llama Stack, you can focus on building your application while we handle the infrastructure complexity, essential capabilities, and provider integrations. From 35c71d5bbed90d0889ae5bc2ba9eb75acd868799 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 22:15:23 -0800 Subject: [PATCH 094/107] Update OpenAPI generator to output discriminator (#848) oneOf should have discriminators so Stainless can generate better code ## Test Plan Going to generate the SDK now and check. --- ...Llama_Stack_Building_AI_Applications.ipynb | 2 +- .../strong_typing/classdef.py | 1 + .../openapi_generator/strong_typing/schema.py | 12 +- docs/resources/llama-stack-spec.html | 115 ++++++++++++++---- docs/resources/llama-stack-spec.yaml | 50 +++++++- llama_stack/distribution/store/registry.py | 2 +- .../client-sdk/tool_runtime/test_rag_tool.py | 12 +- 7 files changed, 159 insertions(+), 35 deletions(-) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb index 58b025db4..0c0f7fa95 100644 --- a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb +++ b/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb @@ -2088,7 +2088,7 @@ "from llama_stack_client.lib.agents.event_logger import EventLogger\n", "from llama_stack_client.types.agent_create_params import AgentConfig\n", "from termcolor import cprint\n", - "from llama_stack_client.types.tool_runtime import DocumentParam as Document\n", + "from llama_stack_client.types import Document\n", "\n", "urls = [\"chat.rst\", \"llama3.rst\", \"datasets.rst\", \"lora_finetune.rst\"]\n", "documents = [\n", diff --git a/docs/openapi_generator/strong_typing/classdef.py b/docs/openapi_generator/strong_typing/classdef.py index c8e6781fd..788ecc7e0 100644 --- a/docs/openapi_generator/strong_typing/classdef.py +++ b/docs/openapi_generator/strong_typing/classdef.py @@ -125,6 +125,7 @@ class JsonSchemaAnyOf(JsonSchemaNode): @dataclass class JsonSchemaOneOf(JsonSchemaNode): oneOf: List["JsonSchemaAny"] + discriminator: Optional[str] JsonSchemaAny = Union[ diff --git a/docs/openapi_generator/strong_typing/schema.py b/docs/openapi_generator/strong_typing/schema.py index 42feeee5a..5aa41b63f 100644 --- a/docs/openapi_generator/strong_typing/schema.py +++ b/docs/openapi_generator/strong_typing/schema.py @@ -36,6 +36,7 @@ from typing import ( ) import jsonschema +from typing_extensions import Annotated from . import docstring from .auxiliary import ( @@ -329,7 +330,6 @@ class JsonSchemaGenerator: if metadata is not None: # type is Annotated[T, ...] typ = typing.get_args(data_type)[0] - schema = self._simple_type_to_schema(typ) if schema is not None: # recognize well-known auxiliary types @@ -446,12 +446,20 @@ class JsonSchemaGenerator: ], } elif origin_type is Union: - return { + discriminator = None + if typing.get_origin(data_type) is Annotated: + discriminator = typing.get_args(data_type)[1].discriminator + ret = { "oneOf": [ self.type_to_schema(union_type) for union_type in typing.get_args(typ) ] } + if discriminator: + ret["discriminator"] = { + "propertyName": discriminator, + } + return ret elif origin_type is Literal: (literal_value,) = typing.get_args(typ) # unpack value of literal type schema = self.type_to_schema(type(literal_value)) diff --git a/docs/resources/llama-stack-spec.html b/docs/resources/llama-stack-spec.html index 139314776..f6024c586 100644 --- a/docs/resources/llama-stack-spec.html +++ b/docs/resources/llama-stack-spec.html @@ -3810,7 +3810,10 @@ { "$ref": "#/components/schemas/TextContentItem" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "Message": { "oneOf": [ @@ -3826,7 +3829,10 @@ { "$ref": "#/components/schemas/CompletionMessage" } - ] + ], + "discriminator": { + "propertyName": "role" + } }, "SamplingParams": { "type": "object", @@ -3842,7 +3848,10 @@ { "$ref": "#/components/schemas/TopKSamplingStrategy" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "max_tokens": { "type": "integer", @@ -4386,7 +4395,10 @@ "bnf" ] } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "ChatCompletionRequest": { "type": "object", @@ -4515,7 +4527,10 @@ { "$ref": "#/components/schemas/ToolCallDelta" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "ImageDelta": { "type": "object", @@ -5019,7 +5034,10 @@ { "$ref": "#/components/schemas/AgentTurnResponseTurnCompletePayload" } - ] + ], + "discriminator": { + "propertyName": "event_type" + } } }, "additionalProperties": false, @@ -5062,7 +5080,10 @@ { "$ref": "#/components/schemas/MemoryRetrievalStep" } - ] + ], + "discriminator": { + "propertyName": "step_type" + } } }, "additionalProperties": false, @@ -5462,7 +5483,10 @@ { "$ref": "#/components/schemas/MemoryRetrievalStep" } - ] + ], + "discriminator": { + "propertyName": "step_type" + } } }, "output_message": { @@ -5612,7 +5636,10 @@ { "$ref": "#/components/schemas/AgentCandidate" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "scoring_params": { "type": "object", @@ -5627,7 +5654,10 @@ { "$ref": "#/components/schemas/BasicScoringFnParams" } - ] + ], + "discriminator": { + "propertyName": "type" + } } }, "num_examples": { @@ -5677,7 +5707,10 @@ { "$ref": "#/components/schemas/AgentCandidate" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "num_examples": { "type": "integer" @@ -5818,7 +5851,10 @@ { "$ref": "#/components/schemas/AppEvalTaskConfig" } - ] + ], + "discriminator": { + "propertyName": "type" + } } }, "additionalProperties": false, @@ -5981,7 +6017,10 @@ { "$ref": "#/components/schemas/MemoryRetrievalStep" } - ] + ], + "discriminator": { + "propertyName": "step_type" + } } }, "additionalProperties": false, @@ -6196,7 +6235,10 @@ { "$ref": "#/components/schemas/AgentTurnInputType" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "StringType": { "type": "object", @@ -6456,7 +6498,10 @@ { "$ref": "#/components/schemas/BasicScoringFnParams" } - ] + ], + "discriminator": { + "propertyName": "type" + } } }, "additionalProperties": false, @@ -7542,7 +7587,10 @@ { "$ref": "#/components/schemas/SpanEndPayload" } - ] + ], + "discriminator": { + "propertyName": "type" + } } }, "additionalProperties": false, @@ -7628,7 +7676,10 @@ { "$ref": "#/components/schemas/StructuredLogEvent" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "ttl_seconds": { "type": "integer" @@ -7958,7 +8009,10 @@ { "$ref": "#/components/schemas/LLMRAGQueryGeneratorConfig" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, "QueryRequest": { "type": "object", @@ -8350,7 +8404,10 @@ { "$ref": "#/components/schemas/BasicScoringFnParams" } - ] + ], + "discriminator": { + "propertyName": "type" + } } }, "additionalProperties": false, @@ -8483,7 +8540,10 @@ { "$ref": "#/components/schemas/AppEvalTaskConfig" } - ] + ], + "discriminator": { + "propertyName": "type" + } } }, "additionalProperties": false, @@ -8632,7 +8692,10 @@ { "$ref": "#/components/schemas/BasicScoringFnParams" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, { "type": "null" @@ -8683,7 +8746,10 @@ { "$ref": "#/components/schemas/BasicScoringFnParams" } - ] + ], + "discriminator": { + "propertyName": "type" + } }, { "type": "null" @@ -8860,7 +8926,10 @@ { "$ref": "#/components/schemas/QATFinetuningConfig" } - ] + ], + "discriminator": { + "propertyName": "type" + } } }, "additionalProperties": false, diff --git a/docs/resources/llama-stack-spec.yaml b/docs/resources/llama-stack-spec.yaml index 1a8c44bc0..21df2d96f 100644 --- a/docs/resources/llama-stack-spec.yaml +++ b/docs/resources/llama-stack-spec.yaml @@ -76,6 +76,8 @@ components: additionalProperties: false properties: step: + discriminator: + propertyName: step_type oneOf: - $ref: '#/components/schemas/InferenceStep' - $ref: '#/components/schemas/ToolExecutionStep' @@ -119,6 +121,8 @@ components: additionalProperties: false properties: payload: + discriminator: + propertyName: event_type oneOf: - $ref: '#/components/schemas/AgentTurnResponseStepStartPayload' - $ref: '#/components/schemas/AgentTurnResponseStepProgressPayload' @@ -137,6 +141,8 @@ components: default: step_complete type: string step_details: + discriminator: + propertyName: step_type oneOf: - $ref: '#/components/schemas/InferenceStep' - $ref: '#/components/schemas/ToolExecutionStep' @@ -258,6 +264,8 @@ components: additionalProperties: false properties: eval_candidate: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/ModelCandidate' - $ref: '#/components/schemas/AgentCandidate' @@ -265,6 +273,8 @@ components: type: integer scoring_params: additionalProperties: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/LLMAsJudgeScoringFnParams' - $ref: '#/components/schemas/RegexParserScoringFnParams' @@ -402,6 +412,8 @@ components: additionalProperties: false properties: eval_candidate: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/ModelCandidate' - $ref: '#/components/schemas/AgentCandidate' @@ -619,6 +631,8 @@ components: title: streamed completion response. type: object ContentDelta: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/TextDelta' - $ref: '#/components/schemas/ImageDelta' @@ -897,6 +911,8 @@ components: type: string type: array task_config: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/BenchmarkEvalTaskConfig' - $ref: '#/components/schemas/AppEvalTaskConfig' @@ -1038,6 +1054,8 @@ components: $ref: '#/components/schemas/InterleavedContentItem' type: array InterleavedContentItem: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/ImageContentItem' - $ref: '#/components/schemas/TextContentItem' @@ -1244,6 +1262,8 @@ components: additionalProperties: false properties: event: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/UnstructuredLogEvent' - $ref: '#/components/schemas/MetricEvent' @@ -1325,6 +1345,8 @@ components: - inserted_context type: object Message: + discriminator: + propertyName: role oneOf: - $ref: '#/components/schemas/UserMessage' - $ref: '#/components/schemas/SystemMessage' @@ -1495,6 +1517,8 @@ components: - total_count type: object ParamType: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/StringType' - $ref: '#/components/schemas/NumberType' @@ -1805,6 +1829,8 @@ components: - max_chunks type: object RAGQueryGeneratorConfig: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/DefaultRAGQueryGeneratorConfig' - $ref: '#/components/schemas/LLMRAGQueryGeneratorConfig' @@ -1922,6 +1948,8 @@ components: description: type: string params: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/LLMAsJudgeScoringFnParams' - $ref: '#/components/schemas/RegexParserScoringFnParams' @@ -2002,6 +2030,8 @@ components: - embedding_model type: object ResponseFormat: + discriminator: + propertyName: type oneOf: - additionalProperties: false properties: @@ -2063,6 +2093,8 @@ components: additionalProperties: false properties: task_config: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/BenchmarkEvalTaskConfig' - $ref: '#/components/schemas/AppEvalTaskConfig' @@ -2130,6 +2162,8 @@ components: default: 1.0 type: number strategy: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/GreedySamplingStrategy' - $ref: '#/components/schemas/TopPSamplingStrategy' @@ -2167,7 +2201,9 @@ components: scoring_functions: additionalProperties: oneOf: - - oneOf: + - discriminator: + propertyName: type + oneOf: - $ref: '#/components/schemas/LLMAsJudgeScoringFnParams' - $ref: '#/components/schemas/RegexParserScoringFnParams' - $ref: '#/components/schemas/BasicScoringFnParams' @@ -2208,7 +2244,9 @@ components: scoring_functions: additionalProperties: oneOf: - - oneOf: + - discriminator: + propertyName: type + oneOf: - $ref: '#/components/schemas/LLMAsJudgeScoringFnParams' - $ref: '#/components/schemas/RegexParserScoringFnParams' - $ref: '#/components/schemas/BasicScoringFnParams' @@ -2246,6 +2284,8 @@ components: - type: object type: object params: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/LLMAsJudgeScoringFnParams' - $ref: '#/components/schemas/RegexParserScoringFnParams' @@ -2503,6 +2543,8 @@ components: - type: object type: object payload: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/SpanStartPayload' - $ref: '#/components/schemas/SpanEndPayload' @@ -2528,6 +2570,8 @@ components: additionalProperties: false properties: algorithm_config: + discriminator: + propertyName: type oneOf: - $ref: '#/components/schemas/LoraFinetuningConfig' - $ref: '#/components/schemas/QATFinetuningConfig' @@ -3115,6 +3159,8 @@ components: type: string steps: items: + discriminator: + propertyName: step_type oneOf: - $ref: '#/components/schemas/InferenceStep' - $ref: '#/components/schemas/ToolExecutionStep' diff --git a/llama_stack/distribution/store/registry.py b/llama_stack/distribution/store/registry.py index 5c0b8b5db..bf0ff3fd0 100644 --- a/llama_stack/distribution/store/registry.py +++ b/llama_stack/distribution/store/registry.py @@ -35,7 +35,7 @@ class DistributionRegistry(Protocol): REGISTER_PREFIX = "distributions:registry" -KEY_VERSION = "v6" +KEY_VERSION = "v7" KEY_FORMAT = f"{REGISTER_PREFIX}:{KEY_VERSION}::" + "{type}:{identifier}" diff --git a/tests/client-sdk/tool_runtime/test_rag_tool.py b/tests/client-sdk/tool_runtime/test_rag_tool.py index baf5b6b40..6e158a1e3 100644 --- a/tests/client-sdk/tool_runtime/test_rag_tool.py +++ b/tests/client-sdk/tool_runtime/test_rag_tool.py @@ -8,7 +8,7 @@ import random import pytest -from llama_stack_client.types.tool_runtime import DocumentParam +from llama_stack_client.types import Document @pytest.fixture(scope="function") @@ -38,22 +38,22 @@ def single_entry_vector_db_registry(llama_stack_client, empty_vector_db_registry @pytest.fixture(scope="session") def sample_documents(): return [ - DocumentParam( + Document( document_id="test-doc-1", content="Python is a high-level programming language.", metadata={"category": "programming", "difficulty": "beginner"}, ), - DocumentParam( + Document( document_id="test-doc-2", content="Machine learning is a subset of artificial intelligence.", metadata={"category": "AI", "difficulty": "advanced"}, ), - DocumentParam( + Document( document_id="test-doc-3", content="Data structures are fundamental to computer science.", metadata={"category": "computer science", "difficulty": "intermediate"}, ), - DocumentParam( + Document( document_id="test-doc-4", content="Neural networks are inspired by biological neural networks.", metadata={"category": "AI", "difficulty": "advanced"}, @@ -148,7 +148,7 @@ def test_vector_db_insert_from_url_and_query( "llama3.rst", ] documents = [ - DocumentParam( + Document( document_id=f"num-{i}", content=f"https://raw.githubusercontent.com/pytorch/torchtune/main/docs/source/tutorials/{url}", mime_type="text/plain", From 28012c51bb37ddd00bd9fcfc9c1c329d3e71739d Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Wed, 22 Jan 2025 22:50:29 -0800 Subject: [PATCH 095/107] update docs for tools and telemetry (#846) # What does this PR do? Added a new Tools doc describing how to use tools and updated the main building agents doc to point to the tools doc. Also updated telemetry doc. https://llama-stack.readthedocs.io/en/tools-doc/building_applications/tools.html --- docs/source/building_applications/index.md | 55 +++-- .../source/building_applications/telemetry.md | 164 +------------- docs/source/building_applications/tools.md | 202 ++++++++++++++++++ 3 files changed, 241 insertions(+), 180 deletions(-) create mode 100644 docs/source/building_applications/tools.md diff --git a/docs/source/building_applications/index.md b/docs/source/building_applications/index.md index 61b7038cd..b9170e092 100644 --- a/docs/source/building_applications/index.md +++ b/docs/source/building_applications/index.md @@ -262,37 +262,58 @@ response = agent.create_turn( ``` ### Adding Tools to Agents +```{toctree} +:hidden: +:maxdepth: 3 -Agents can be enhanced with various tools: +tools +``` -1. **Search**: Web search capabilities through providers like Brave -2. **Code Interpreter**: Execute code snippets -3. **RAG**: Memory and document retrieval -4. **Function Calling**: Custom function execution -5. **WolframAlpha**: Mathematical computations -6. **Photogen**: Image generation +Agents can be enhanced with various tools. For detailed information about available tools, their configuration, and providers, see the [Tools](tools.md) documentation. -Example of configuring an agent with tools: +Tools are configured through the `toolgroups` parameter in the agent configuration. Each tool group can be specified either as a string or with additional arguments: ```python +from llama_stack_client.lib.agents.agent import Agent +from llama_stack_client.types.agent_create_params import AgentConfig + agent_config = AgentConfig( model="Llama3.2-3B-Instruct", - tools=[ + instructions="You are a helpful assistant", + # Configure tool groups + toolgroups=[ + # Simple string format + "builtin::code_interpreter", + # With arguments format { - "type": "brave_search", - "api_key": "YOUR_API_KEY", - "engine": "brave" - }, - { - "type": "code_interpreter", - "enable_inline_code_execution": True + "name": "builtin::websearch", + "args": { + "max_results": 5 + } } ], tool_choice="auto", - tool_prompt_format="json" + tool_prompt_format="json", + # Optional safety configuration + input_shields=["content_safety"], + output_shields=["content_safety"], + # Control the inference loop + max_infer_iters=10, + sampling_params={ + "strategy": { + "type": "top_p", + "temperature": 0.7, + "top_p": 0.95 + }, + "max_tokens": 2048 + } ) + +agent = Agent(client, agent_config) ``` +For details on available tool groups, providers, and their configuration options, refer to the [Tools](tools.md) documentation. + ## Building RAG-Enhanced Agents One of the most powerful patterns is combining agents with RAG capabilities. Here's a complete example: diff --git a/docs/source/building_applications/telemetry.md b/docs/source/building_applications/telemetry.md index 70c54ac98..ee640398b 100644 --- a/docs/source/building_applications/telemetry.md +++ b/docs/source/building_applications/telemetry.md @@ -1,8 +1,4 @@ # Telemetry -```{note} -The telemetry system is currently experimental and subject to change. We welcome feedback and contributions to help improve it. -``` - The Llama Stack telemetry system provides comprehensive tracing, metrics, and logging capabilities. It supports multiple sink types including OpenTelemetry, SQLite, and Console output. @@ -44,58 +40,6 @@ structured_log_event = SpanStartPayload( - **SQLite**: Store events in a local SQLite database. This is needed if you want to query the events later through the Llama Stack API. - **Console**: Print events to the console. -## APIs - -The telemetry API is designed to be flexible for different user flows like debugging/visualization in UI, monitoring, and saving traces to datasets. -The telemetry system exposes the following HTTP endpoints: - -### Log Event -```http -POST /telemetry/log-event -``` -Logs a telemetry event (unstructured log, metric, or structured log) with optional TTL. - -### Query Traces -```http -POST /telemetry/query-traces -``` -Retrieves traces based on filters with pagination support. Parameters: -- `attribute_filters`: List of conditions to filter traces -- `limit`: Maximum number of traces to return (default: 100) -- `offset`: Number of traces to skip (default: 0) -- `order_by`: List of fields to sort by - -### Get Span Tree -```http -POST /telemetry/get-span-tree -``` -Retrieves a hierarchical view of spans starting from a specific span. Parameters: -- `span_id`: ID of the root span to retrieve -- `attributes_to_return`: Optional list of specific attributes to include -- `max_depth`: Optional maximum depth of the span tree to return - -### Query Spans -```http -POST /telemetry/query-spans -``` -Retrieves spans matching specified filters and returns selected attributes. Parameters: -- `attribute_filters`: List of conditions to filter traces -- `attributes_to_return`: List of specific attributes to include in results -- `max_depth`: Optional maximum depth of spans to traverse (default: no limit) - -Returns a flattened list of spans with requested attributes. - -### Save Spans to Dataset -This is useful for saving traces to a dataset for running evaluations. For example, you can save the input/output of each span that is part of an agent session/turn to a dataset and then run an eval task on it. See example in [Example: Save Spans to Dataset](#example-save-spans-to-dataset). -```http -POST /telemetry/save-spans-to-dataset -``` -Queries spans and saves their attributes to a dataset. Parameters: -- `attribute_filters`: List of conditions to filter traces -- `attributes_to_save`: List of span attributes to save to the dataset -- `dataset_id`: ID of the dataset to save to -- `max_depth`: Optional maximum depth of spans to traverse (default: no limit) - ## Providers ### Meta-Reference Provider @@ -133,110 +77,4 @@ Once the Jaeger instance is running, you can visualize traces by navigating to h ## Querying Traces Stored in SQLIte -The `sqlite` sink allows you to query traces without an external system. Here are some example queries: - -Querying Traces for a agent session -The client SDK is not updated to support the new telemetry API. It will be updated soon. You can manually query traces using the following curl command: - -``` bash - curl -X POST 'http://localhost:8321/alpha/telemetry/query-traces' \ --H 'Content-Type: application/json' \ --d '{ - "attribute_filters": [ - { - "key": "session_id", - "op": "eq", - "value": "dd667b87-ca4b-4d30-9265-5a0de318fc65" }], - "limit": 100, - "offset": 0, - "order_by": ["start_time"] - - [ - { - "trace_id": "6902f54b83b4b48be18a6f422b13e16f", - "root_span_id": "5f37b85543afc15a", - "start_time": "2024-12-04T08:08:30.501587", - "end_time": "2024-12-04T08:08:36.026463" - }, - ........ -] -}' - -``` - -Querying spans for a specifc root span id - -``` bash -curl -X POST 'http://localhost:8321/alpha/telemetry/get-span-tree' \ --H 'Content-Type: application/json' \ --d '{ "span_id" : "6cceb4b48a156913", "max_depth": 2 }' - -{ - "span_id": "6cceb4b48a156913", - "trace_id": "dafa796f6aaf925f511c04cd7c67fdda", - "parent_span_id": "892a66d726c7f990", - "name": "retrieve_rag_context", - "start_time": "2024-12-04T09:28:21.781995", - "end_time": "2024-12-04T09:28:21.913352", - "attributes": { - "input": [ - "{\"role\":\"system\",\"content\":\"You are a helpful assistant\"}", - "{\"role\":\"user\",\"content\":\"What are the top 5 topics that were explained in the documentation? Only list succinct bullet points.\",\"context\":null}" - ] - }, - "children": [ - { - "span_id": "1a2df181854064a8", - "trace_id": "dafa796f6aaf925f511c04cd7c67fdda", - "parent_span_id": "6cceb4b48a156913", - "name": "MemoryRouter.query_documents", - "start_time": "2024-12-04T09:28:21.787620", - "end_time": "2024-12-04T09:28:21.906512", - "attributes": { - "input": null - }, - "children": [], - "status": "ok" - } - ], - "status": "ok" -} - -``` - -## Example: Save Spans to Dataset -Save all spans for a specific agent session to a dataset. -``` bash -curl -X POST 'http://localhost:8321/alpha/telemetry/save-spans-to-dataset' \ --H 'Content-Type: application/json' \ --d '{ - "attribute_filters": [ - { - "key": "session_id", - "op": "eq", - "value": "dd667b87-ca4b-4d30-9265-5a0de318fc65" - } - ], - "attributes_to_save": ["input", "output"], - "dataset_id": "my_dataset", - "max_depth": 10 -}' -``` - -Save all spans for a specific agent turn to a dataset. -```bash -curl -X POST 'http://localhost:8321/alpha/telemetry/save-spans-to-dataset' \ --H 'Content-Type: application/json' \ --d '{ - "attribute_filters": [ - { - "key": "turn_id", - "op": "eq", - "value": "123e4567-e89b-12d3-a456-426614174000" - } - ], - "attributes_to_save": ["input", "output"], - "dataset_id": "my_dataset", - "max_depth": 10 -}' -``` +The `sqlite` sink allows you to query traces without an external system. Here are some example queries. Refer to the notebook at [Llama Stack Building AI Applications](https://github.com/meta-llama/llama-stack/blob/main/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb) for more examples on how to query traces and spaces. diff --git a/docs/source/building_applications/tools.md b/docs/source/building_applications/tools.md new file mode 100644 index 000000000..1339a14ae --- /dev/null +++ b/docs/source/building_applications/tools.md @@ -0,0 +1,202 @@ +# Tools + +Tools are functions that can be invoked by an agent to perform tasks. They are organized into tool groups and registered with specific providers. Each tool group represents a collection of related tools from a single provider. They are organized into groups so that state can be externalized: the collection operates on the same state typically. +An example of this would be a "db_access" tool group that contains tools for interacting with a database. "list_tables", "query_table", "insert_row" could be examples of tools in this group. + +Tools are treated as any other resource in llama stack like models. You can register them, have providers for them etc. + +When instatiating an agent, you can provide it a list of tool groups that it has access to. Agent gets the corresponding tool definitions for the specified tool groups and passes them along to the model. + +Refer to the [Building AI Applications](https://github.com/meta-llama/llama-stack/blob/main/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb) notebook for more examples on how to use tools. + +## Types of Tool Group providers + +There are three types of providers for tool groups that are supported by Llama Stack. + +1. Built-in providers +2. Model Context Protocol (MCP) providers +3. Client provided tools + +### Built-in providers + +Built-in providers come packaged with Llama Stack. These providers provide common functionalities like web search, code interpretation, and computational capabilities. + +#### Web Search providers +There are three web search providers that are supported by Llama Stack. + +1. Brave Search +2. Bing Search +3. Tavily Search + +Example client SDK call to register a "websearch" toolgroup that is provided by brave-search. + +```python +# Register Brave Search tool group +client.toolgroups.register( + toolgroup_id="builtin::websearch", + provider_id="brave-search", + args={"max_results": 5} +) +``` + +The tool requires an API key which can be provided either in the configuration or through the request header `X-LlamaStack-Provider-Data`. The format of the header is `{"_api_key": }`. + + + +#### Code Interpreter + +The Code Interpreter allows execution of Python code within a controlled environment. + +```python +# Register Code Interpreter tool group +client.toolgroups.register( + toolgroup_id="builtin::code_interpreter", + provider_id="code_interpreter" +) +``` + +Features: +- Secure execution environment using `bwrap` sandboxing +- Matplotlib support for generating plots +- Disabled dangerous system operations +- Configurable execution timeouts + +#### WolframAlpha + +The WolframAlpha tool provides access to computational knowledge through the WolframAlpha API. + +```python +# Register WolframAlpha tool group +client.toolgroups.register( + toolgroup_id="builtin::wolfram_alpha", + provider_id="wolfram-alpha" +) +``` + +Example usage: +```python +result = client.tool_runtime.invoke_tool( + tool_name="wolfram_alpha", + args={"query": "solve x^2 + 2x + 1 = 0"} +) +``` + +#### Memory + +The Memory tool enables retrieval of context from various types of memory banks (vector, key-value, keyword, and graph). + +```python +# Register Memory tool group +client.toolgroups.register( + toolgroup_id="builtin::memory", + provider_id="memory", + args={ + "max_chunks": 5, + "max_tokens_in_context": 4096 + } +) +``` + +Features: +- Support for multiple memory bank types +- Configurable query generation +- Context retrieval with token limits + + +> **Note:** By default, llama stack run.yaml defines toolgroups for web search, code interpreter and memory, that are provided by tavily-search, code-interpreter and memory providers. + +## Model Context Protocol (MCP) Tools + +MCP tools are special tools that can interact with llama stack over model context protocol. These tools are dynamically discovered from an MCP endpoint and can be used to extend the agent's capabilities. + +Refer to https://github.com/modelcontextprotocol/server for available MCP servers. + +```python +# Register MCP tools +client.toolgroups.register( + toolgroup_id="builtin::filesystem", + provider_id="model-context-protocol", + mcp_endpoint=URL(uri="http://localhost:8000/sse"), +) +``` + +MCP tools require: +- A valid MCP endpoint URL +- The endpoint must implement the Model Context Protocol +- Tools are discovered dynamically from the endpoint + + +## Tools provided by the client + +These tools are registered along with the agent config and are specific to the agent for which they are registered. The main difference between these tools and the tools provided by the built-in providers is that the execution of these tools is handled by the client and the agent transfers the tool call to the client and waits for the result from the client. + +```python +# Example agent config with client provided tools +config = AgentConfig( + toolgroups=[ + "builtin::websearch", + ], + client_tools=[ + ToolDef(name="client_tool", description="Client provided tool") + ] +) +``` + +Refer to [llama-stack-apps](https://github.com/meta-llama/llama-stack-apps/blob/main/examples/agents/e2e_loop_with_custom_tools.py) for an example of how to use client provided tools. + +## Tool Structure + +Each tool has the following components: + +- `name`: Unique identifier for the tool +- `description`: Human-readable description of the tool's functionality +- `parameters`: List of parameters the tool accepts + - `name`: Parameter name + - `parameter_type`: Data type (string, number, etc.) + - `description`: Parameter description + - `required`: Whether the parameter is required (default: true) + - `default`: Default value if any + +Example tool definition: +```python +{ + "name": "web_search", + "description": "Search the web for information", + "parameters": [ + { + "name": "query", + "parameter_type": "string", + "description": "The query to search for", + "required": True + } + ] +} +``` + +## Tool Invocation + +Tools can be invoked using the `invoke_tool` method: + +```python +result = client.tool_runtime.invoke_tool( + tool_name="web_search", + kwargs={"query": "What is the capital of France?"} +) +``` + +The result contains: +- `content`: The tool's output +- `error_message`: Optional error message if the tool failed +- `error_code`: Optional error code if the tool failed + +## Listing Available Tools + +You can list all available tools or filter by tool group: + +```python +# List all tools +all_tools = client.tools.list_tools() + +# List tools in a specific group +group_tools = client.tools.list_tools(toolgroup_id="search_tools") +``` From 4d7c8c797ffb9262d55aa1d88dbd24d2e698638d Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 22:54:13 -0800 Subject: [PATCH 096/107] Kill colons --- llama_stack/templates/cerebras/report.md | 8 ++++---- llama_stack/templates/fireworks/remote-hosted-report.md | 6 +++--- llama_stack/templates/fireworks/report.md | 8 ++++---- llama_stack/templates/ollama/report.md | 8 ++++---- llama_stack/templates/tgi/report.md | 8 ++++---- llama_stack/templates/together/report.md | 8 ++++---- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/llama_stack/templates/cerebras/report.md b/llama_stack/templates/cerebras/report.md index c65cd4979..7c09474b1 100644 --- a/llama_stack/templates/cerebras/report.md +++ b/llama_stack/templates/cerebras/report.md @@ -1,6 +1,6 @@ # Report for cerebras distribution -## Supported Models: +## Supported Models | Model Descriptor | cerebras | |:---|:---| | meta-llama/Llama-3-8B-Instruct | ❌ | @@ -18,7 +18,7 @@ | meta-llama/Llama-Guard-3-8B | ❌ | | meta-llama/Llama-Guard-2-8B | ❌ | -## Inference: +## Inference | Model | API | Capability | Test | Status | |:----- |:-----|:-----|:-----|:-----| | Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ | @@ -31,12 +31,12 @@ | Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ | | Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ❌ | -## Vector_io: +## Vector IO | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /retrieve | | test_vector_db_retrieve | ✅ | -## Agents: +## Agents | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /create_agent_turn | rag | test_rag_agent | ✅ | diff --git a/llama_stack/templates/fireworks/remote-hosted-report.md b/llama_stack/templates/fireworks/remote-hosted-report.md index fb338ba13..2f3c882b7 100644 --- a/llama_stack/templates/fireworks/remote-hosted-report.md +++ b/llama_stack/templates/fireworks/remote-hosted-report.md @@ -1,6 +1,6 @@ # Report for fireworks distribution -## Supported Models: +## Supported Models | Model Descriptor | fireworks | |:---|:---| | meta-llama/Llama-3-8B-Instruct | ❌ | @@ -18,7 +18,7 @@ | meta-llama/Llama-Guard-3-8B | ❌ | | meta-llama/Llama-Guard-2-8B | ❌ | -## Inference: +## Inference | Model | API | Capability | Test | Status | |:----- |:-----|:-----|:-----|:-----| | Text | /chat_completion | streaming | test_text_chat_completion_streaming | ❌ | @@ -37,7 +37,7 @@ | /insert, /query | inline | test_memory_bank_insert_inline_and_query | ❌ | | /insert, /query | url | test_memory_bank_insert_from_url_and_query | ❌ | -## Agents: +## Agents | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | create_agent_turn | rag | test_rag_agent | ❌ | diff --git a/llama_stack/templates/fireworks/report.md b/llama_stack/templates/fireworks/report.md index 1c5550bf4..00e8f6a55 100644 --- a/llama_stack/templates/fireworks/report.md +++ b/llama_stack/templates/fireworks/report.md @@ -1,6 +1,6 @@ # Report for fireworks distribution -## Supported Models: +## Supported Models | Model Descriptor | fireworks | |:---|:---| | Llama-3-8B-Instruct | ❌ | @@ -18,7 +18,7 @@ | Llama-Guard-3-8B | ✅ | | Llama-Guard-2-8B | ❌ | -## Inference: +## Inference | Model | API | Capability | Test | Status | |:----- |:-----|:-----|:-----|:-----| | Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ | @@ -31,12 +31,12 @@ | Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ | | Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ✅ | -## Vector_io: +## Vector IO | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /retrieve | | test_vector_db_retrieve | ✅ | -## Agents: +## Agents | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /create_agent_turn | rag | test_rag_agent | ✅ | diff --git a/llama_stack/templates/ollama/report.md b/llama_stack/templates/ollama/report.md index 0d370b8ec..724809a59 100644 --- a/llama_stack/templates/ollama/report.md +++ b/llama_stack/templates/ollama/report.md @@ -1,6 +1,6 @@ # Report for ollama distribution -## Supported Models: +## Supported Models | Model Descriptor | ollama | |:---|:---| | Llama-3-8B-Instruct | ❌ | @@ -18,7 +18,7 @@ | Llama-Guard-3-8B | ✅ | | Llama-Guard-2-8B | ❌ | -## Inference: +## Inference | Model | API | Capability | Test | Status | |:----- |:-----|:-----|:-----|:-----| | Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ | @@ -31,12 +31,12 @@ | Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ | | Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ✅ | -## Vector_io: +## Vector IO | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /retrieve | | test_vector_db_retrieve | ✅ | -## Agents: +## Agents | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /create_agent_turn | rag | test_rag_agent | ✅ | diff --git a/llama_stack/templates/tgi/report.md b/llama_stack/templates/tgi/report.md index 1f76ff692..b0f5d88a2 100644 --- a/llama_stack/templates/tgi/report.md +++ b/llama_stack/templates/tgi/report.md @@ -1,6 +1,6 @@ # Report for tgi distribution -## Supported Models: +## Supported Models | Model Descriptor | tgi | |:---|:---| | Llama-3-8B-Instruct | ✅ | @@ -18,7 +18,7 @@ | Llama-Guard-3-8B | ✅ | | Llama-Guard-2-8B | ✅ | -## Inference: +## Inference | Model | API | Capability | Test | Status | |:----- |:-----|:-----|:-----|:-----| | Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ | @@ -31,12 +31,12 @@ | Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ | | Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ✅ | -## Vector_io: +## Vector IO | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /retrieve | | test_vector_db_retrieve | ✅ | -## Agents: +## Agents | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /create_agent_turn | rag | test_rag_agent | ✅ | diff --git a/llama_stack/templates/together/report.md b/llama_stack/templates/together/report.md index 10891f4e5..b5339c640 100644 --- a/llama_stack/templates/together/report.md +++ b/llama_stack/templates/together/report.md @@ -1,6 +1,6 @@ # Report for together distribution -## Supported Models: +## Supported Models | Model Descriptor | together | |:---|:---| | Llama-3-8B-Instruct | ❌ | @@ -18,7 +18,7 @@ | Llama-Guard-3-8B | ✅ | | Llama-Guard-2-8B | ❌ | -## Inference: +## Inference | Model | API | Capability | Test | Status | |:----- |:-----|:-----|:-----|:-----| | Llama-3.1-8B-Instruct | /chat_completion | streaming | test_text_chat_completion_streaming | ✅ | @@ -31,12 +31,12 @@ | Llama-3.1-8B-Instruct | /completion | non_streaming | test_text_completion_non_streaming | ✅ | | Llama-3.1-8B-Instruct | /completion | structured_output | test_text_completion_structured_output | ✅ | -## Vector_io: +## Vector IO | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /retrieve | | test_vector_db_retrieve | ✅ | -## Agents: +## Agents | API | Capability | Test | Status | |:-----|:-----|:-----|:-----| | /create_agent_turn | rag | test_rag_agent | ✅ | From 910717c1fd8033daa4cc53b845beb648a2d71e51 Mon Sep 17 00:00:00 2001 From: Aidan Do Date: Thu, 23 Jan 2025 17:58:27 +1100 Subject: [PATCH 097/107] Add vLLM raw completions API (#823) # What does this PR do? Adds raw completions API to vLLM ## Test Plan
Setup ```bash # Run vllm server conda create -n vllm python=3.12 -y conda activate vllm pip install vllm # Run llamastack conda create --name llamastack-vllm python=3.10 conda activate llamastack-vllm export INFERENCE_MODEL=meta-llama/Llama-3.2-3B-Instruct && \ pip install -e . && \ pip install --no-cache --index-url https://pypi.org/simple/ --extra-index-url https://test.pypi.org/simple/ llama-stack==0.1.0rc7 && \ llama stack build --template remote-vllm --image-type conda && \ llama stack run ./distributions/remote-vllm/run.yaml \ --port 5000 \ --env INFERENCE_MODEL=$INFERENCE_MODEL \ --env VLLM_URL=http://localhost:8000/v1 | tee -a llama-stack.log ```
Integration ```bash # Run conda activate llamastack-vllm export VLLM_URL=http://localhost:8000/v1 pip install pytest pytest_html pytest_asyncio aiosqlite pytest llama_stack/providers/tests/inference/test_text_inference.py -v -k vllm # Results llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_model_list[-vllm_remote] PASSED [ 11%] llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_completion[-vllm_remote] PASSED [ 22%] llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_completion_logprobs[-vllm_remote] SKIPPED [ 33%] llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_completion_structured_output[-vllm_remote] SKIPPED [ 44%] llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_chat_completion_non_streaming[-vllm_remote] PASSED [ 55%] llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_structured_output[-vllm_remote] PASSED [ 66%] llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_chat_completion_streaming[-vllm_remote] PASSED [ 77%] llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_chat_completion_with_tool_calling[-vllm_remote] PASSED [ 88%] llama_stack/providers/tests/inference/test_text_inference.py::TestInference::test_chat_completion_with_tool_calling_streaming[-vllm_remote] PASSED [100%] ====================================== 7 passed, 2 skipped, 99 deselected, 1 warning in 9.80s ====================================== ```
Manual ```bash # Install pip install --no-cache --index-url https://pypi.org/simple/ --extra-index-url https://test.pypi.org/simple/ llama-stack==0.1.0rc7 ``` Apply this diff ```diff diff --git a/llama_stack/distribution/server/server.py b/llama_stack/distribution/server/server.py index 8dbb193..95173e2 100644 --- a/llama_stack/distribution/server/server.py +++ b/llama_stack/distribution/server/server.py @@ -250,7 +250,7 @@ class ClientVersionMiddleware: server_version_parts = tuple( map(int, self.server_version.split(".")[:2]) ) - if client_version_parts != server_version_parts: + if False and client_version_parts != server_version_parts: async def send_version_error(send): await send( diff --git a/llama_stack/templates/remote-vllm/run.yaml b/llama_stack/templates/remote-vllm/run.yaml index 4eac4da..32eb50e 100644 --- a/llama_stack/templates/remote-vllm/run.yaml +++ b/llama_stack/templates/remote-vllm/run.yaml @@ -94,7 +94,8 @@ metadata_store: type: sqlite db_path: ${env.SQLITE_STORE_DIR:~/.llama/distributions/remote-vllm}/registry.db models: -- metadata: {} +- metadata: + llama_model: meta-llama/Llama-3.2-3B-Instruct model_id: ${env.INFERENCE_MODEL} provider_id: vllm-inference model_type: llm ``` Test 1: ```python from llama_stack_client import LlamaStackClient client = LlamaStackClient( base_url="http://localhost:5000", ) response = client.inference.completion( model_id="meta-llama/Llama-3.2-3B-Instruct", content="Hello, world client!", ) print(response) ``` Test 2 ``` from llama_stack_client import LlamaStackClient client = LlamaStackClient( base_url="http://localhost:5000", ) response = client.inference.completion( model_id="meta-llama/Llama-3.2-3B-Instruct", content="Hello, world client!", stream=True, ) for chunk in response: print(chunk.delta, end="", flush=True) ``` ``` I'm excited to introduce you to our latest project, a comprehensive guide to the best coffee shops in [City]. As a coffee connoisseur, you're in luck because we've scoured the city to bring you the top picks for the perfect cup of joe. In this guide, we'll take you on a journey through the city's most iconic coffee shops, highlighting their unique features, must-try drinks, and insider tips from the baristas themselves. From cozy cafes to trendy cafes, we've got you covered. **Top 5 Coffee Shops in [City]** 1. **The Daily Grind**: This beloved institution has been serving up expertly crafted pour-overs and lattes for over 10 years. Their expert baristas are always happy to guide you through their menu, which features a rotating selection of single-origin beans from around the world... ```
## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- .../providers/remote/inference/vllm/vllm.py | 36 ++++++++++++++++++- .../tests/inference/test_text_inference.py | 1 + 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/llama_stack/providers/remote/inference/vllm/vllm.py b/llama_stack/providers/remote/inference/vllm/vllm.py index 1dbb4ecfa..0cf16f013 100644 --- a/llama_stack/providers/remote/inference/vllm/vllm.py +++ b/llama_stack/providers/remote/inference/vllm/vllm.py @@ -41,6 +41,8 @@ from llama_stack.providers.utils.inference.openai_compat import ( get_sampling_options, process_chat_completion_response, process_chat_completion_stream_response, + process_completion_response, + process_completion_stream_response, ) from llama_stack.providers.utils.inference.prompt_adapter import ( chat_completion_request_to_prompt, @@ -92,7 +94,19 @@ class VLLMInferenceAdapter(Inference, ModelsProtocolPrivate): stream: Optional[bool] = False, logprobs: Optional[LogProbConfig] = None, ) -> Union[CompletionResponse, CompletionResponseStreamChunk]: - raise NotImplementedError("Completion not implemented for vLLM") + model = await self.model_store.get_model(model_id) + request = CompletionRequest( + model=model.provider_resource_id, + content=content, + sampling_params=sampling_params, + response_format=response_format, + stream=stream, + logprobs=logprobs, + ) + if stream: + return self._stream_completion(request) + else: + return await self._nonstream_completion(request) async def chat_completion( self, @@ -154,6 +168,26 @@ class VLLMInferenceAdapter(Inference, ModelsProtocolPrivate): ): yield chunk + async def _nonstream_completion( + self, request: CompletionRequest + ) -> CompletionResponse: + params = await self._get_params(request) + r = self.client.completions.create(**params) + return process_completion_response(r, self.formatter) + + async def _stream_completion(self, request: CompletionRequest) -> AsyncGenerator: + params = await self._get_params(request) + + # Wrapper for async generator similar + async def _to_async_generator(): + stream = self.client.completions.create(**params) + for chunk in stream: + yield chunk + + stream = _to_async_generator() + async for chunk in process_completion_stream_response(stream, self.formatter): + yield chunk + async def register_model(self, model: Model) -> Model: model = await self.register_helper.register_model(model) res = self.client.models.list() diff --git a/llama_stack/providers/tests/inference/test_text_inference.py b/llama_stack/providers/tests/inference/test_text_inference.py index c39556b8e..e1052c289 100644 --- a/llama_stack/providers/tests/inference/test_text_inference.py +++ b/llama_stack/providers/tests/inference/test_text_inference.py @@ -118,6 +118,7 @@ class TestInference: "remote::fireworks", "remote::nvidia", "remote::cerebras", + "remote::vllm", ): pytest.skip("Other inference providers don't support completion() yet") From 3d14a3d46fbf5df8a589e9a9b2a78ef024faff84 Mon Sep 17 00:00:00 2001 From: Ashwin Bharambe Date: Wed, 22 Jan 2025 22:54:13 -0800 Subject: [PATCH 098/107] Kill colons --- tests/client-sdk/report.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/client-sdk/report.py b/tests/client-sdk/report.py index de50efa46..cf7a84d7f 100644 --- a/tests/client-sdk/report.py +++ b/tests/client-sdk/report.py @@ -147,7 +147,7 @@ class Report: def pytest_sessionfinish(self, session): report = [] report.append(f"# Report for {self.image_name} distribution") - report.append("\n## Supported Models:") + report.append("\n## Supported Models") header = f"| Model Descriptor | {self.image_name} |" dividor = "|:---|:---|" @@ -180,7 +180,7 @@ class Report: rows.append(row) report.extend(rows) - report.append("\n## Inference:") + report.append("\n## Inference") test_table = [ "| Model | API | Capability | Test | Status |", "|:----- |:-----|:-----|:-----|:-----|", @@ -205,9 +205,10 @@ class Report: report.extend(test_table) + name_map = {Api.vector_io: "Vector IO", Api.agents: "Agents"} for api_group in [Api.vector_io, Api.agents]: - api_capitalized = api_group.name.capitalize() - report.append(f"\n## {api_capitalized}:") + api_capitalized = name_map[api_group] + report.append(f"\n## {api_capitalized}") test_table = [ "| API | Capability | Test | Status |", "|:-----|:-----|:-----|:-----|", From 82a28f3a2494163d95b1fa09ce9113aac25a4ff5 Mon Sep 17 00:00:00 2001 From: Sixian Yi Date: Thu, 23 Jan 2025 00:17:16 -0800 Subject: [PATCH 099/107] update doc for client-sdk testing (#849) As title ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- docs/source/contributing/new_api_provider.md | 7 ++++--- tests/client-sdk/README.md | 21 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests/client-sdk/README.md diff --git a/docs/source/contributing/new_api_provider.md b/docs/source/contributing/new_api_provider.md index 3fa875c50..f1b50da98 100644 --- a/docs/source/contributing/new_api_provider.md +++ b/docs/source/contributing/new_api_provider.md @@ -13,11 +13,12 @@ This guide contains references to walk you through adding a new API provider. ## Testing your newly added API providers -1. Start with an _integration test_ for your provider. That means we will instantiate the real provider, pass it real configuration and if it is a remote service, we will actually hit the remote service. We **strongly** discourage mocking for these tests at the provider level. Llama Stack is first and foremost about integration so we need to make sure stuff works end-to-end. See {repopath}`llama_stack/providers/tests/inference/test_text_inference.py` for an example. +1. Start with an _integration test_ for your provider. That means we will instantiate the real provider, pass it real configuration and if it is a remote service, we will actually hit the remote service. We **strongly** discourage mocking for these tests at the provider level. Llama Stack is first and foremost about integration so we need to make sure stuff works end-to-end. See {repopath}`tests/client-sdk` for an example. -2. In addition, if you want to unit test functionality within your provider, feel free to do so. You can find some tests in `tests/` but they aren't well-supported so far. -3. Test with a client-server Llama Stack setup. (a) Start a Llama Stack server with your own distribution which includes the new provider. (b) Send a client request to the server. See `llama_stack/apis//client.py` for how this is done. These client scripts can serve as lightweight tests. +2. In addition, if you want to unit test functionality within your provider, feel free to do so. You can find some tests in {repopath}`llama_stack/providers/tests/inference/test_text_inference.py`. + +3. Test with a client-server Llama Stack setup. (a) Start a Llama Stack server with your own distribution which includes the new provider. (b) Send a client request to the server. These client scripts can serve as lightweight tests. You can find more complex client scripts [llama-stack-apps](https://github.com/meta-llama/llama-stack-apps/tree/main) repo. Note down which scripts works and do not work with your distribution. diff --git a/tests/client-sdk/README.md b/tests/client-sdk/README.md new file mode 100644 index 000000000..2edf6d3c8 --- /dev/null +++ b/tests/client-sdk/README.md @@ -0,0 +1,21 @@ +# Llama Stack Integration Tests +You can run llama stack integration tests on either a Llama Stack Library or a Llama Stack endpoint. + +To test on a Llama Stack library with certain configuration, run +```bash +LLAMA_STACK_CONFIG=./llama_stack/templates/cerebras/run.yaml +pytest -s -v tests/client-sdk/inference/test_inference.py +``` + +To test on a Llama Stack endpoint, run +```bash +LLAMA_STACK_BASE_URL=http//localhost:8089 +pytest -s -v tests/client-sdk/inference/test_inference.py +``` + + +## Common options +Depending on the API, there are custom options enabled +- For tests in `inference/` and `agents/, we support `--inference-model` (to be used in text inference tests) and `--vision-inference-model` (only used in image inference tests) overrides +- For tests in `vector_io/`, we support `--embedding-model` override +- For tests in `safety/`, we support `--safety-shield` override From bfbd773b545632bab3441bbcf6a9b890f19b9876 Mon Sep 17 00:00:00 2001 From: Sixian Yi Date: Thu, 23 Jan 2025 01:06:39 -0800 Subject: [PATCH 100/107] remove test report --- llama_stack/providers/tests/test_report.md | 70 ---------------------- 1 file changed, 70 deletions(-) delete mode 100644 llama_stack/providers/tests/test_report.md diff --git a/llama_stack/providers/tests/test_report.md b/llama_stack/providers/tests/test_report.md deleted file mode 100644 index 1153ef772..000000000 --- a/llama_stack/providers/tests/test_report.md +++ /dev/null @@ -1,70 +0,0 @@ -### Fireworks -| filepath | function | passed | SUBTOTAL | -| -------------------------------------------------------------- | ------------------------------------------------------------------ | -----: | -------: | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 1 | -| llama_stack/providers/tests/inference/test_vision_inference.py | TestVisionModelInference.test_vision_chat_completion_non_streaming | 2 | 2 | -| llama_stack/providers/tests/inference/test_vision_inference.py | TestVisionModelInference.test_vision_chat_completion_streaming | 1 | 1 | -| TOTAL | | 9 | 9 | - - - -### Together -| filepath | function | passed | SUBTOTAL | -| -------------------------------------------------------------- | ------------------------------------------------------------------ | -----: | -------: | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 1 | -| llama_stack/providers/tests/inference/test_vision_inference.py | TestVisionModelInference.test_vision_chat_completion_non_streaming | 2 | 2 | -| llama_stack/providers/tests/inference/test_vision_inference.py | TestVisionModelInference.test_vision_chat_completion_streaming | 1 | 1 | -| TOTAL | | 9 | 9 | - - -### vLLM - -| filepath | function | passed | skipped | SUBTOTAL | -| ------------------------------------------------------------ | -------------------------------------------------------------- | -----: | ------: | -------: | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_model_list | 1 | 0 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 0 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 0 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 0 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 0 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 0 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion | 0 | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion_logprobs | 0 | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion_structured_output | 0 | 1 | 1 | -| TOTAL | | 6 | 3 | 9 | - -### Ollama -| filepath | function | passed | SUBTOTAL | -| ------------------------------------------------------------ | -------------------------------------------------------------- | -----: | -------: | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_completion | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 1 | -| llama_stack/providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 1 | -| TOTAL | | 6 | 6 | - - -### tgi - -| filepath | function | passed | skipped | SUBTOTAL | -| ------------------------------------------------ | -------------------------------------------------------------- | -----: | ------: | -------: | -| providers/tests/inference/test_text_inference.py | TestInference.test_model_list | 1 | 0 | 1 | -| providers/tests/inference/test_text_inference.py | TestInference.test_completion | 1 | 0 | 1 | -| providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_non_streaming | 1 | 0 | 1 | -| providers/tests/inference/test_text_inference.py | TestInference.test_structured_output | 1 | 0 | 1 | -| providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_streaming | 1 | 0 | 1 | -| providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling | 1 | 0 | 1 | -| providers/tests/inference/test_text_inference.py | TestInference.test_chat_completion_with_tool_calling_streaming | 1 | 0 | 1 | -| providers/tests/inference/test_text_inference.py | TestInference.test_completion_logprobs | 0 | 1 | 1 | -| providers/tests/inference/test_text_inference.py | TestInference.test_completion_structured_output | 0 | 1 | 1 | -| TOTAL | | 7 | 2 | 9 | From e44a1a68f188a49be490a89c537d67512f77ab72 Mon Sep 17 00:00:00 2001 From: raghotham Date: Thu, 23 Jan 2025 07:15:47 -0800 Subject: [PATCH 101/107] Delete docs/to_situate directory (#851) # What does this PR do? No need for the cookbook now. Removing the folder - [ ] Addresses issue (#issue) ## Test Plan Please describe: - tests you ran to verify your changes with result summaries. - provide instructions so it can be reproduced. ## Sources Please link relevant resources if necessary. ## Before submitting - [ ] This PR fixes a typo or improves the docs (you can dismiss the other checks if that's the case). - [ ] Ran pre-commit to handle lint / formatting issues. - [ ] Read the [contributor guideline](https://github.com/meta-llama/llama-stack/blob/main/CONTRIBUTING.md), Pull Request section? - [ ] Updated relevant documentation. - [ ] Wrote necessary unit or integration tests. --- docs/to_situate/developer_cookbook.md | 41 --------------------------- 1 file changed, 41 deletions(-) delete mode 100644 docs/to_situate/developer_cookbook.md diff --git a/docs/to_situate/developer_cookbook.md b/docs/to_situate/developer_cookbook.md deleted file mode 100644 index 56ebd7a76..000000000 --- a/docs/to_situate/developer_cookbook.md +++ /dev/null @@ -1,41 +0,0 @@ -# Llama Stack Developer Cookbook - -Based on your developer needs, below are references to guides to help you get started. - -### Hosted Llama Stack Endpoint -* Developer Need: I want to connect to a Llama Stack endpoint to build my applications. -* Effort: 1min -* Guide: - - Checkout our [DeepLearning course](https://www.deeplearning.ai/short-courses/introducing-multimodal-llama-3-2) on building with Llama Stack apps on pre-hosted Llama Stack endpoint. - - -### Local meta-reference Llama Stack Server -* Developer Need: I want to start a local Llama Stack server with my GPU using meta-reference implementations. -* Effort: 5min -* Guide: - - Please see our [meta-reference-gpu](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/meta-reference-gpu.html) on starting up a meta-reference Llama Stack server. - -### Llama Stack Server with Remote Providers -* Developer need: I want a Llama Stack distribution with a remote provider. -* Effort: 10min -* Guide - - Please see our [Distributions Guide](https://llama-stack.readthedocs.io/en/latest/concepts/index.html#distributions) on starting up distributions with remote providers. - - -### On-Device (iOS) Llama Stack -* Developer Need: I want to use Llama Stack on-Device -* Effort: 1.5hr -* Guide: - - Please see our [iOS Llama Stack SDK](./ios_sdk.md) implementations - -### Assemble your own Llama Stack Distribution -* Developer Need: I want to assemble my own distribution with API providers to my likings -* Effort: 30min -* Guide - - Please see our [Building Distribution](./building_distro.md) guide for assembling your own Llama Stack distribution with your choice of API providers. - -### Adding a New API Provider -* Developer Need: I want to add a new API provider to Llama Stack. -* Effort: 3hr -* Guide - - Please see our [Adding a New API Provider](https://llama-stack.readthedocs.io/en/latest/contributing/new_api_provider.html) guide for adding a new API provider. From 25a70ca4dc465cecfb02f4c43447476392ce5833 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Thu, 23 Jan 2025 08:19:51 -0800 Subject: [PATCH 102/107] Fixed distro documentation (#852) More docs --- docs/source/distributions/building_distro.md | 4 +- docs/source/distributions/index.md | 54 +++++++----------- .../distributions/ondevice_distro/ios_sdk.md | 3 - .../remote_hosted_distro/index.md | 3 - docs/source/distributions/selection.md | 56 +++++++++++++++++++ .../self_hosted_distro/ollama.md | 3 - .../self_hosted_distro/remote-vllm.md | 3 - .../distributions/self_hosted_distro/tgi.md | 4 -- .../self_hosted_distro/together.md | 3 - docs/source/index.md | 1 + 10 files changed, 79 insertions(+), 55 deletions(-) create mode 100644 docs/source/distributions/selection.md diff --git a/docs/source/distributions/building_distro.md b/docs/source/distributions/building_distro.md index 83069aa05..9034a1811 100644 --- a/docs/source/distributions/building_distro.md +++ b/docs/source/distributions/building_distro.md @@ -4,7 +4,7 @@ This guide will walk you through the steps to get started with building a Llama Stack distribution from scratch with your choice of API providers. -## Llama Stack Build +### Llama Stack Build In order to build your own distribution, we recommend you clone the `llama-stack` repository. @@ -373,7 +373,7 @@ After this step is successful, you should be able to find the built container im :::: -## Running your Stack server +### Running your Stack server Now, let's start the Llama Stack Distribution Server. You will need the YAML configuration file which was written out at the end by the `llama stack build` step. ``` diff --git a/docs/source/distributions/index.md b/docs/source/distributions/index.md index 5d84ebd9e..64fec543f 100644 --- a/docs/source/distributions/index.md +++ b/docs/source/distributions/index.md @@ -1,41 +1,27 @@ -# Starting a Llama Stack +# Starting a Llama Stack Server + +You can run a Llama Stack server in one of the following ways: + +**As a Library**: + +This is the simplest way to get started. Using Llama Stack as a library means you do not need to start a server. This is especially useful when you are not running inference locally and relying on an external inference service (eg. fireworks, together, groq, etc.) See [Using Llama Stack as a Library](importing_as_library) + + +**Docker**: + +Another simple way to start interacting with Llama Stack is to just spin up docker which is pre-built with all the providers you need. We provide a number of pre-built Docker containers so you can start a Llama Stack server instantly. You can also build your own custom Docker container. Which distribution to choose depends on the hardware you have. See [Selection of a Distribution](distributions/selection) for more details. + + +**Conda**: + +Lastly, if you have a custom or an advanced setup or you are developing on Llama Stackyou can also build a custom Llama Stack server. Using `llama stack build` and `llama stack run` you can build/run a custom Llama Stack server containing the exact combination of providers you wish. We have also provided various templates to make getting started easier. See [Building a Custom Distribution](building_distro) for more details. + + ```{toctree} -:maxdepth: 3 +:maxdepth: 1 :hidden: importing_as_library building_distro configuration ``` - -You can instantiate a Llama Stack in one of the following ways: -- **As a Library**: this is the simplest, especially if you are using an external inference service. See [Using Llama Stack as a Library](importing_as_library) -- **Docker**: we provide a number of pre-built Docker containers so you can start a Llama Stack server instantly. You can also build your own custom Docker container. -- **Conda**: finally, you can build a custom Llama Stack server using `llama stack build` containing the exact combination of providers you wish. We have provided various templates to make getting started easier. - -Which templates / distributions to choose depends on the hardware you have for running LLM inference. - -- **Do you have access to a machine with powerful GPUs?** -If so, we suggest: - - {dockerhub}`distribution-remote-vllm` ([Guide](self_hosted_distro/remote-vllm)) - - {dockerhub}`distribution-meta-reference-gpu` ([Guide](self_hosted_distro/meta-reference-gpu)) - - {dockerhub}`distribution-tgi` ([Guide](self_hosted_distro/tgi)) - - {dockerhub} `distribution-nvidia` ([Guide](self_hosted_distro/nvidia)) - -- **Are you running on a "regular" desktop machine?** -If so, we suggest: - - {dockerhub}`distribution-ollama` ([Guide](self_hosted_distro/ollama)) - -- **Do you have an API key for a remote inference provider like Fireworks, Together, etc.?** If so, we suggest: - - {dockerhub}`distribution-together` ([Guide](self_hosted_distro/together)) - - {dockerhub}`distribution-fireworks` ([Guide](self_hosted_distro/fireworks)) - -- **Do you want to run Llama Stack inference on your iOS / Android device** If so, we suggest: - - [iOS SDK](ondevice_distro/ios_sdk) - - [Android](ondevice_distro/android_sdk) - -- **Do you want a hosted Llama Stack endpoint?** If so, we suggest: - - [Remote-Hosted Llama Stack Endpoints](remote_hosted_distro/index) - - -You can also build your own [custom distribution](building_distro). diff --git a/docs/source/distributions/ondevice_distro/ios_sdk.md b/docs/source/distributions/ondevice_distro/ios_sdk.md index c9d3a89b5..ffaf74533 100644 --- a/docs/source/distributions/ondevice_distro/ios_sdk.md +++ b/docs/source/distributions/ondevice_distro/ios_sdk.md @@ -1,6 +1,3 @@ ---- -orphan: true ---- # iOS SDK We offer both remote and on-device use of Llama Stack in Swift via two components: diff --git a/docs/source/distributions/remote_hosted_distro/index.md b/docs/source/distributions/remote_hosted_distro/index.md index 0f86bf73f..2fbe381af 100644 --- a/docs/source/distributions/remote_hosted_distro/index.md +++ b/docs/source/distributions/remote_hosted_distro/index.md @@ -1,6 +1,3 @@ ---- -orphan: true ---- # Remote-Hosted Distributions Remote-Hosted distributions are available endpoints serving Llama Stack API that you can directly connect to. diff --git a/docs/source/distributions/selection.md b/docs/source/distributions/selection.md new file mode 100644 index 000000000..08c3e985a --- /dev/null +++ b/docs/source/distributions/selection.md @@ -0,0 +1,56 @@ +# List of Distributions + +Here are a list of distributions you can use to start a Llama Stack server that are provided out of the box. + +## Selection of a Distribution / Template + +Which templates / distributions to choose depends on the hardware you have for running LLM inference. + +- **Do you want a hosted Llama Stack endpoint?** If so, we suggest leveraging our partners who host Llama Stack endpoints. Namely, _fireworks.ai_ and _together.xyz_. + - Read more about it here - [Remote-Hosted Endpoints](remote_hosted_distro/index). + + +- **Do you have access to machines with GPUs?** If you wish to run Llama Stack locally or on a cloud instance and host your own Llama Stack endpoint, we suggest: + - {dockerhub}`distribution-remote-vllm` ([Guide](self_hosted_distro/remote-vllm)) + - {dockerhub}`distribution-meta-reference-gpu` ([Guide](self_hosted_distro/meta-reference-gpu)) + - {dockerhub}`distribution-tgi` ([Guide](self_hosted_distro/tgi)) + - {dockerhub}`distribution-nvidia` ([Guide](self_hosted_distro/nvidia)) + +- **Are you running on a "regular" desktop or laptop ?** We suggest using the ollama templte for quick prototyping and get started without having to worry about needing GPUs. + - {dockerhub}`distribution-ollama` ([link](self_hosted_distro/ollama)) + +- **Do you have an API key for a remote inference provider like Fireworks, Together, etc.?** If so, we suggest: + - {dockerhub}`distribution-together` ([Guide](self_hosted_distro/together)) + - {dockerhub}`distribution-fireworks` ([Guide](self_hosted_distro/fireworks)) + +- **Do you want to run Llama Stack inference on your iOS / Android device** Lastly, we also provide templates for running Llama Stack inference on your iOS / Android device: + - [iOS SDK](ondevice_distro/ios_sdk) + - [Android](ondevice_distro/android_sdk) + + +- **If none of the above fit your needs, you can also build your own [custom distribution](building_distro).** + +### Distribution Details + +```{toctree} +:maxdepth: 1 + +remote_hosted_distro/index +self_hosted_distro/remote-vllm +self_hosted_distro/meta-reference-gpu +self_hosted_distro/tgi +self_hosted_distro/nvidia +self_hosted_distro/ollama +self_hosted_distro/together +self_hosted_distro/fireworks +ondevice_distro/index +``` + +### On-Device Distributions + +```{toctree} +:maxdepth: 1 + +ondevice_distro/ios_sdk +ondevice_distro/android_sdk +``` diff --git a/docs/source/distributions/self_hosted_distro/ollama.md b/docs/source/distributions/self_hosted_distro/ollama.md index b03a5ee16..93f4adfb3 100644 --- a/docs/source/distributions/self_hosted_distro/ollama.md +++ b/docs/source/distributions/self_hosted_distro/ollama.md @@ -1,6 +1,3 @@ ---- -orphan: true ---- # Ollama Distribution ```{toctree} diff --git a/docs/source/distributions/self_hosted_distro/remote-vllm.md b/docs/source/distributions/self_hosted_distro/remote-vllm.md index 95dd392c1..1638e9b11 100644 --- a/docs/source/distributions/self_hosted_distro/remote-vllm.md +++ b/docs/source/distributions/self_hosted_distro/remote-vllm.md @@ -1,6 +1,3 @@ ---- -orphan: true ---- # Remote vLLM Distribution ```{toctree} :maxdepth: 2 diff --git a/docs/source/distributions/self_hosted_distro/tgi.md b/docs/source/distributions/self_hosted_distro/tgi.md index 1883b926c..5a709d0a8 100644 --- a/docs/source/distributions/self_hosted_distro/tgi.md +++ b/docs/source/distributions/self_hosted_distro/tgi.md @@ -1,7 +1,3 @@ ---- -orphan: true ---- - # TGI Distribution ```{toctree} diff --git a/docs/source/distributions/self_hosted_distro/together.md b/docs/source/distributions/self_hosted_distro/together.md index 2d5c8fc77..707f5be7a 100644 --- a/docs/source/distributions/self_hosted_distro/together.md +++ b/docs/source/distributions/self_hosted_distro/together.md @@ -1,6 +1,3 @@ ---- -orphan: true ---- # Together Distribution ```{toctree} diff --git a/docs/source/index.md b/docs/source/index.md index 7e7977738..bc4666be3 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -57,6 +57,7 @@ introduction/index getting_started/index concepts/index distributions/index +distributions/selection building_applications/index benchmark_evaluations/index playground/index From 8a686270e9c6e315ee049cb9f1f201b75bb5faee Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 23 Jan 2025 10:09:09 -0800 Subject: [PATCH 103/107] remove getting started notebook (#853) # What does this PR do? This notebook is no longer updated and we should be using https://github.com/meta-llama/llama-stack/blob/main/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb --- docs/getting_started.ipynb | 4675 ------------------------------------ 1 file changed, 4675 deletions(-) delete mode 100644 docs/getting_started.ipynb diff --git a/docs/getting_started.ipynb b/docs/getting_started.ipynb deleted file mode 100644 index 1db7c0280..000000000 --- a/docs/getting_started.ipynb +++ /dev/null @@ -1,4675 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "c1e7571c", - "metadata": { - "id": "c1e7571c" - }, - "source": [ - "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1F2ksmkoGQPa4pzRjMOE6BXWeOxWFIW6n?usp=sharing)\n", - "\n", - "# Llama Stack - Building AI Applications\n", - "\n", - "\"drawing\"\n", - "\n", - "[Llama Stack](https://github.com/meta-llama/llama-stack) defines and standardizes the set of core building blocks needed to bring generative AI applications to market. These building blocks are presented in the form of interoperable APIs with a broad set of Service Providers providing their implementations.\n", - "\n", - "Read more about the project: https://llama-stack.readthedocs.io/en/latest/index.html\n", - "\n", - "In this guide, we will showcase how you can build LLM-powered agentic applications using Llama Stack.\n" - ] - }, - { - "cell_type": "markdown", - "id": "4CV1Q19BDMVw", - "metadata": { - "id": "4CV1Q19BDMVw" - }, - "source": [ - "## 1. Getting started with Llama Stack" - ] - }, - { - "cell_type": "markdown", - "id": "K4AvfUAJZOeS", - "metadata": { - "id": "K4AvfUAJZOeS" - }, - "source": [ - "### 1.1. Create TogetherAI account\n", - "\n", - "\n", - "In order to run inference for the llama models, you will need to use an inference provider. Llama stack supports a number of inference [providers](https://github.com/meta-llama/llama-stack/tree/main/llama_stack/providers/remote/inference).\n", - "\n", - "\n", - "In this showcase, we will use [together.ai](https://www.together.ai/) as the inference provider. So, you would first get an API key from Together if you dont have one already.\n", - "\n", - "Steps [here](https://docs.google.com/document/d/1Vg998IjRW_uujAPnHdQ9jQWvtmkZFt74FldW2MblxPY/edit?usp=sharing).\n", - "\n", - "You can also use Fireworks.ai or even Ollama if you would like to.\n", - "\n", - "\n", - "\n", - "> **Note:** Set the API Key in the Secrets of this notebook\n", - "\n" - ] - }, - { - "cell_type": "markdown", - "id": "oDUB7M_qe-Gs", - "metadata": { - "id": "oDUB7M_qe-Gs" - }, - "source": [ - "### 1.2. Install Llama Stack\n", - "\n", - "We will now start with installing the [llama-stack pypi package](https://pypi.org/project/llama-stack).\n", - "\n", - "In addition, we will install [bubblewrap](https://github.com/containers/bubblewrap), a low level light-weight container framework that runs in the user namespace. We will use it to execute code generated by Llama in one of the examples." - ] - }, - { - "cell_type": "code", - "execution_count": 42, - "id": "J2kGed0R5PSf", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "J2kGed0R5PSf", - "outputId": "7d543c6f-623d-4911-b9a7-4ed24d5b82f2" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Reading package lists... Done\n", - "Building dependency tree... Done\n", - "Reading state information... Done\n", - "bubblewrap is already the newest version (0.6.1-1ubuntu0.1).\n", - "0 upgraded, 0 newly installed, 0 to remove and 49 not upgraded.\n", - "Requirement already satisfied: llama-stack in /usr/local/lib/python3.10/dist-packages (0.0.61)\n", - "Requirement already satisfied: blobfile in /usr/local/lib/python3.10/dist-packages (from llama-stack) (3.0.0)\n", - "Requirement already satisfied: fire in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.7.0)\n", - "Requirement already satisfied: httpx in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.28.1)\n", - "Requirement already satisfied: huggingface-hub in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.26.5)\n", - "Requirement already satisfied: llama-models>=0.0.61 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.0.61)\n", - "Requirement already satisfied: llama-stack-client>=0.0.61 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.0.61)\n", - "Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.10/dist-packages (from llama-stack) (3.0.48)\n", - "Requirement already satisfied: python-dotenv in /usr/local/lib/python3.10/dist-packages (from llama-stack) (1.0.1)\n", - "Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.10.3)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.32.3)\n", - "Requirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (from llama-stack) (13.9.4)\n", - "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from llama-stack) (75.1.0)\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.5.0)\n", - "Requirement already satisfied: PyYAML in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama-stack) (6.0.2)\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama-stack) (3.1.4)\n", - "Requirement already satisfied: tiktoken in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama-stack) (0.8.0)\n", - "Requirement already satisfied: Pillow in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama-stack) (10.4.0)\n", - "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (3.7.1)\n", - "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (8.1.7)\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (1.9.0)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (2.2.2)\n", - "Requirement already satisfied: pyaml in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (24.12.1)\n", - "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (1.3.1)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (4.66.6)\n", - "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (4.12.2)\n", - "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (2024.8.30)\n", - "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (1.0.7)\n", - "Requirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (3.10)\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx->llama-stack) (0.14.0)\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama-stack) (0.7.0)\n", - "Requirement already satisfied: pydantic-core==2.27.1 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama-stack) (2.27.1)\n", - "Requirement already satisfied: pycryptodomex>=3.8 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (3.21.0)\n", - "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (2.2.3)\n", - "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (5.3.0)\n", - "Requirement already satisfied: filelock>=3.0 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (3.16.1)\n", - "Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama-stack) (2024.9.0)\n", - "Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama-stack) (24.2)\n", - "Requirement already satisfied: wcwidth in /usr/local/lib/python3.10/dist-packages (from prompt-toolkit->llama-stack) (0.2.13)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->llama-stack) (3.4.0)\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack) (3.0.0)\n", - "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack) (2.18.0)\n", - "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->llama-stack-client>=0.0.61->llama-stack) (1.2.2)\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich->llama-stack) (0.1.2)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->llama-models>=0.0.61->llama-stack) (3.0.2)\n", - "Requirement already satisfied: numpy>=1.22.4 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama-stack) (1.26.4)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama-stack) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama-stack) (2024.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama-stack) (2024.2)\n", - "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.10/dist-packages (from tiktoken->llama-models>=0.0.61->llama-stack) (2024.9.11)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas->llama-stack-client>=0.0.61->llama-stack) (1.17.0)\n" - ] - } - ], - "source": [ - "!apt-get install -y bubblewrap\n", - "!pip install -U llama-stack" - ] - }, - { - "cell_type": "markdown", - "id": "414301dc", - "metadata": { - "id": "414301dc" - }, - "source": [ - "### 1.3. Configure Llama Stack for Together\n", - "\n", - "\n", - "Llama Stack is architected as a collection of lego blocks which can be assembled as needed.\n", - "\n", - "\n", - "Typically, llama stack is available as a server with an endpoint that you can hit. We call this endpoint a [Distribution](https://llama-stack.readthedocs.io/en/latest/concepts/index.html#distributions). Partners like Together and Fireworks offer their own Llama Stack Distribution endpoints.\n", - "\n", - "In this showcase, we are going to use llama stack inline as a library. So, given a particular set of providers, we must first package up the right set of dependencies. We have a template to use Together as an inference provider and [faiss](https://ai.meta.com/tools/faiss/) for memory/RAG.\n", - "\n", - "We will run `llama stack build` to deploy all dependencies." - ] - }, - { - "cell_type": "code", - "execution_count": 43, - "id": "HaepEZXCDgif", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "HaepEZXCDgif", - "outputId": "9c268d26-7444-4741-f14d-3911eea8e4eb" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: llama-stack in /usr/local/lib/python3.10/dist-packages (0.0.61)\r\n", - "Requirement already satisfied: blobfile in /usr/local/lib/python3.10/dist-packages (from llama-stack) (3.0.0)\r\n", - "Requirement already satisfied: fire in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.7.0)\r\n", - "Requirement already satisfied: httpx in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.28.1)\r\n", - "Requirement already satisfied: huggingface-hub in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.26.5)\r\n", - "Requirement already satisfied: llama-models>=0.0.61 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.0.61)\r\n", - "Requirement already satisfied: llama-stack-client>=0.0.61 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (0.0.61)\r\n", - "Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.10/dist-packages (from llama-stack) (3.0.48)\r\n", - "Requirement already satisfied: python-dotenv in /usr/local/lib/python3.10/dist-packages (from llama-stack) (1.0.1)\r\n", - "Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.10.3)\r\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.32.3)\r\n", - "Requirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (from llama-stack) (13.9.4)\r\n", - "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from llama-stack) (75.1.0)\r\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from llama-stack) (2.5.0)\r\n", - "Requirement already satisfied: PyYAML in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama-stack) (6.0.2)\r\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama-stack) (3.1.4)\r\n", - "Requirement already satisfied: tiktoken in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama-stack) (0.8.0)\r\n", - "Requirement already satisfied: Pillow in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama-stack) (10.4.0)\r\n", - "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (3.7.1)\r\n", - "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (8.1.7)\r\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (1.9.0)\r\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (2.2.2)\r\n", - "Requirement already satisfied: pyaml in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (24.12.1)\r\n", - "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (1.3.1)\r\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (4.66.6)\r\n", - "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama-stack) (4.12.2)\r\n", - "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (2024.8.30)\r\n", - "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (1.0.7)\r\n", - "Requirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx->llama-stack) (3.10)\r\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx->llama-stack) (0.14.0)\r\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama-stack) (0.7.0)\r\n", - "Requirement already satisfied: pydantic-core==2.27.1 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama-stack) (2.27.1)\r\n", - "Requirement already satisfied: pycryptodomex>=3.8 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (3.21.0)\r\n", - "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (2.2.3)\r\n", - "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (5.3.0)\r\n", - "Requirement already satisfied: filelock>=3.0 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama-stack) (3.16.1)\n", - "Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama-stack) (2024.9.0)\n", - "Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama-stack) (24.2)\n", - "Requirement already satisfied: wcwidth in /usr/local/lib/python3.10/dist-packages (from prompt-toolkit->llama-stack) (0.2.13)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->llama-stack) (3.4.0)\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack) (3.0.0)\n", - "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama-stack) (2.18.0)\n", - "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->llama-stack-client>=0.0.61->llama-stack) (1.2.2)\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich->llama-stack) (0.1.2)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->llama-models>=0.0.61->llama-stack) (3.0.2)\n", - "Requirement already satisfied: numpy>=1.22.4 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama-stack) (1.26.4)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama-stack) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama-stack) (2024.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama-stack) (2024.2)\n", - "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.10/dist-packages (from tiktoken->llama-models>=0.0.61->llama-stack) (2024.9.11)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas->llama-stack-client>=0.0.61->llama-stack) (1.17.0)\n", - "Installing pip dependencies\n", - "Requirement already satisfied: pillow in /usr/local/lib/python3.10/dist-packages (10.4.0)\n", - "Requirement already satisfied: transformers in /usr/local/lib/python3.10/dist-packages (4.46.3)\n", - "Requirement already satisfied: psycopg2-binary in /usr/local/lib/python3.10/dist-packages (2.9.10)\n", - "Requirement already satisfied: aiosqlite in /usr/local/lib/python3.10/dist-packages (0.20.0)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (4.66.6)\n", - "Requirement already satisfied: pypdf in /usr/local/lib/python3.10/dist-packages (5.1.0)\n", - "Requirement already satisfied: numpy in /usr/local/lib/python3.10/dist-packages (1.26.4)\n", - "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.10/dist-packages (1.5.2)\n", - "Requirement already satisfied: redis in /usr/local/lib/python3.10/dist-packages (5.2.1)\n", - "Requirement already satisfied: opentelemetry-sdk in /usr/local/lib/python3.10/dist-packages (1.28.2)\n", - "Requirement already satisfied: sentencepiece in /usr/local/lib/python3.10/dist-packages (0.2.0)\n", - "Requirement already satisfied: blobfile in /usr/local/lib/python3.10/dist-packages (3.0.0)\n", - "Requirement already satisfied: together in /usr/local/lib/python3.10/dist-packages (1.3.5)\n", - "Requirement already satisfied: openai in /usr/local/lib/python3.10/dist-packages (1.54.5)\n", - "Requirement already satisfied: faiss-cpu in /usr/local/lib/python3.10/dist-packages (1.9.0.post1)\n", - "Requirement already satisfied: autoevals in /usr/local/lib/python3.10/dist-packages (0.0.110)\n", - "Requirement already satisfied: chardet in /usr/local/lib/python3.10/dist-packages (5.2.0)\n", - "Requirement already satisfied: nltk in /usr/local/lib/python3.10/dist-packages (3.9.1)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (2.2.2)\n", - "Requirement already satisfied: opentelemetry-exporter-otlp-proto-http in /usr/local/lib/python3.10/dist-packages (1.28.2)\n", - "Requirement already satisfied: datasets in /usr/local/lib/python3.10/dist-packages (3.2.0)\n", - "Requirement already satisfied: matplotlib in /usr/local/lib/python3.10/dist-packages (3.8.0)\n", - "Requirement already satisfied: scipy in /usr/local/lib/python3.10/dist-packages (1.13.1)\n", - "Requirement already satisfied: chromadb-client in /usr/local/lib/python3.10/dist-packages (0.5.23)\n", - "Requirement already satisfied: fastapi in /usr/local/lib/python3.10/dist-packages (0.115.6)\n", - "Requirement already satisfied: fire in /usr/local/lib/python3.10/dist-packages (0.7.0)\n", - "Requirement already satisfied: httpx in /usr/local/lib/python3.10/dist-packages (0.28.1)\n", - "Requirement already satisfied: uvicorn in /usr/local/lib/python3.10/dist-packages (0.32.1)\n", - "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from transformers) (3.16.1)\n", - "Requirement already satisfied: huggingface-hub<1.0,>=0.23.2 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.26.5)\n", - "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from transformers) (24.2)\n", - "Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.10/dist-packages (from transformers) (6.0.2)\n", - "Requirement already satisfied: regex!=2019.12.17 in /usr/local/lib/python3.10/dist-packages (from transformers) (2024.9.11)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from transformers) (2.32.3)\n", - "Requirement already satisfied: tokenizers<0.21,>=0.20 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.20.3)\n", - "Requirement already satisfied: safetensors>=0.4.1 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.4.5)\n", - "Requirement already satisfied: typing_extensions>=4.0 in /usr/local/lib/python3.10/dist-packages (from aiosqlite) (4.12.2)\n", - "Requirement already satisfied: joblib>=1.2.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (1.4.2)\n", - "Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.10/dist-packages (from scikit-learn) (3.5.0)\n", - "Requirement already satisfied: async-timeout>=4.0.3 in /usr/local/lib/python3.10/dist-packages (from redis) (4.0.3)\n", - "Requirement already satisfied: opentelemetry-api==1.28.2 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-sdk) (1.28.2)\n", - "Requirement already satisfied: opentelemetry-semantic-conventions==0.49b2 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-sdk) (0.49b2)\n", - "Requirement already satisfied: deprecated>=1.2.6 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-api==1.28.2->opentelemetry-sdk) (1.2.15)\n", - "Requirement already satisfied: importlib-metadata<=8.5.0,>=6.0 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-api==1.28.2->opentelemetry-sdk) (8.5.0)\n", - "Requirement already satisfied: pycryptodomex>=3.8 in /usr/local/lib/python3.10/dist-packages (from blobfile) (3.21.0)\n", - "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.10/dist-packages (from blobfile) (2.2.3)\n", - "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.10/dist-packages (from blobfile) (5.3.0)\n", - "Requirement already satisfied: aiohttp<4.0.0,>=3.9.3 in /usr/local/lib/python3.10/dist-packages (from together) (3.11.10)\n", - "Requirement already satisfied: click<9.0.0,>=8.1.7 in /usr/local/lib/python3.10/dist-packages (from together) (8.1.7)\n", - "Requirement already satisfied: eval-type-backport<0.3.0,>=0.1.3 in /usr/local/lib/python3.10/dist-packages (from together) (0.2.0)\n", - "Requirement already satisfied: pyarrow>=10.0.1 in /usr/local/lib/python3.10/dist-packages (from together) (17.0.0)\n", - "Requirement already satisfied: pydantic<3.0.0,>=2.6.3 in /usr/local/lib/python3.10/dist-packages (from together) (2.10.3)\n", - "Requirement already satisfied: rich<14.0.0,>=13.8.1 in /usr/local/lib/python3.10/dist-packages (from together) (13.9.4)\n", - "Requirement already satisfied: tabulate<0.10.0,>=0.9.0 in /usr/local/lib/python3.10/dist-packages (from together) (0.9.0)\n", - "Requirement already satisfied: typer<0.14,>=0.9 in /usr/local/lib/python3.10/dist-packages (from together) (0.13.1)\n", - "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from openai) (3.7.1)\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from openai) (1.9.0)\n", - "Requirement already satisfied: jiter<1,>=0.4.0 in /usr/local/lib/python3.10/dist-packages (from openai) (0.8.2)\n", - "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from openai) (1.3.1)\n", - "Requirement already satisfied: chevron in /usr/local/lib/python3.10/dist-packages (from autoevals) (0.14.0)\n", - "Requirement already satisfied: levenshtein in /usr/local/lib/python3.10/dist-packages (from autoevals) (0.26.1)\n", - "Requirement already satisfied: braintrust_core==0.0.54 in /usr/local/lib/python3.10/dist-packages (from autoevals) (0.0.54)\n", - "Requirement already satisfied: jsonschema in /usr/local/lib/python3.10/dist-packages (from autoevals) (4.23.0)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas) (2024.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas) (2024.2)\n", - "Requirement already satisfied: googleapis-common-protos~=1.52 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-exporter-otlp-proto-http) (1.66.0)\n", - "Requirement already satisfied: opentelemetry-exporter-otlp-proto-common==1.28.2 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-exporter-otlp-proto-http) (1.28.2)\n", - "Requirement already satisfied: opentelemetry-proto==1.28.2 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-exporter-otlp-proto-http) (1.28.2)\n", - "Requirement already satisfied: protobuf<6.0,>=5.0 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-proto==1.28.2->opentelemetry-exporter-otlp-proto-http) (5.29.1)\n", - "Requirement already satisfied: dill<0.3.9,>=0.3.0 in /usr/local/lib/python3.10/dist-packages (from datasets) (0.3.8)\n", - "Requirement already satisfied: xxhash in /usr/local/lib/python3.10/dist-packages (from datasets) (3.5.0)\n", - "Requirement already satisfied: multiprocess<0.70.17 in /usr/local/lib/python3.10/dist-packages (from datasets) (0.70.16)\n", - "Requirement already satisfied: fsspec<=2024.9.0,>=2023.1.0 in /usr/local/lib/python3.10/dist-packages (from fsspec[http]<=2024.9.0,>=2023.1.0->datasets) (2024.9.0)\n", - "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.3.1)\n", - "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (0.12.1)\n", - "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (4.55.3)\n", - "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (1.4.7)\n", - "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.10/dist-packages (from matplotlib) (3.2.0)\n", - "Requirement already satisfied: opentelemetry-exporter-otlp-proto-grpc>=1.2.0 in /usr/local/lib/python3.10/dist-packages (from chromadb-client) (1.28.2)\n", - "Requirement already satisfied: overrides>=7.3.1 in /usr/local/lib/python3.10/dist-packages (from chromadb-client) (7.7.0)\n", - "Requirement already satisfied: posthog>=2.4.0 in /usr/local/lib/python3.10/dist-packages (from chromadb-client) (3.7.4)\n", - "Requirement already satisfied: tenacity>=8.2.3 in /usr/local/lib/python3.10/dist-packages (from chromadb-client) (9.0.0)\n", - "Requirement already satisfied: orjson>=3.9.12 in /usr/local/lib/python3.10/dist-packages (from chromadb-client) (3.10.12)\n", - "Requirement already satisfied: starlette<0.42.0,>=0.40.0 in /usr/local/lib/python3.10/dist-packages (from fastapi) (0.41.3)\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from fire) (2.5.0)\n", - "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx) (2024.8.30)\n", - "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx) (1.0.7)\n", - "Requirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx) (3.10)\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx) (0.14.0)\n", - "Requirement already satisfied: aiohappyeyeballs>=2.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (2.4.4)\n", - "Requirement already satisfied: aiosignal>=1.1.2 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (1.3.1)\n", - "Requirement already satisfied: attrs>=17.3.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (24.2.0)\n", - "Requirement already satisfied: frozenlist>=1.1.1 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (1.5.0)\n", - "Requirement already satisfied: multidict<7.0,>=4.5 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (6.1.0)\n", - "Requirement already satisfied: propcache>=0.2.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (0.2.1)\n", - "Requirement already satisfied: yarl<2.0,>=1.17.0 in /usr/local/lib/python3.10/dist-packages (from aiohttp<4.0.0,>=3.9.3->together) (1.18.3)\n", - "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->openai) (1.2.2)\n", - "Requirement already satisfied: wrapt<2,>=1.10 in /usr/local/lib/python3.10/dist-packages (from deprecated>=1.2.6->opentelemetry-api==1.28.2->opentelemetry-sdk) (1.17.0)\n", - "Requirement already satisfied: grpcio<2.0.0,>=1.63.2 in /usr/local/lib/python3.10/dist-packages (from opentelemetry-exporter-otlp-proto-grpc>=1.2.0->chromadb-client) (1.68.1)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from posthog>=2.4.0->chromadb-client) (1.17.0)\n", - "Requirement already satisfied: monotonic>=1.5 in /usr/local/lib/python3.10/dist-packages (from posthog>=2.4.0->chromadb-client) (1.6)\n", - "Requirement already satisfied: backoff>=1.10.0 in /usr/local/lib/python3.10/dist-packages (from posthog>=2.4.0->chromadb-client) (2.2.1)\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic<3.0.0,>=2.6.3->together) (0.7.0)\n", - "Requirement already satisfied: pydantic-core==2.27.1 in /usr/local/lib/python3.10/dist-packages (from pydantic<3.0.0,>=2.6.3->together) (2.27.1)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (3.4.0)\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich<14.0.0,>=13.8.1->together) (3.0.0)\n", - "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich<14.0.0,>=13.8.1->together) (2.18.0)\n", - "Requirement already satisfied: shellingham>=1.3.0 in /usr/local/lib/python3.10/dist-packages (from typer<0.14,>=0.9->together) (1.5.4)\n", - "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /usr/local/lib/python3.10/dist-packages (from jsonschema->autoevals) (2024.10.1)\n", - "Requirement already satisfied: referencing>=0.28.4 in /usr/local/lib/python3.10/dist-packages (from jsonschema->autoevals) (0.35.1)\n", - "Requirement already satisfied: rpds-py>=0.7.1 in /usr/local/lib/python3.10/dist-packages (from jsonschema->autoevals) (0.22.3)\n", - "Requirement already satisfied: rapidfuzz<4.0.0,>=3.9.0 in /usr/local/lib/python3.10/dist-packages (from levenshtein->autoevals) (3.10.1)\n", - "Requirement already satisfied: zipp>=3.20 in /usr/local/lib/python3.10/dist-packages (from importlib-metadata<=8.5.0,>=6.0->opentelemetry-api==1.28.2->opentelemetry-sdk) (3.21.0)\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich<14.0.0,>=13.8.1->together) (0.1.2)\n", - "sentence-transformers --no-deps\n", - "Requirement already satisfied: sentence-transformers in /usr/local/lib/python3.10/dist-packages (3.2.1)\n", - "torch --index-url https://download.pytorch.org/whl/cpu\n", - "Looking in indexes: https://download.pytorch.org/whl/cpu\n", - "Requirement already satisfied: torch in /usr/local/lib/python3.10/dist-packages (2.5.1+cu121)\n", - "Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from torch) (3.16.1)\n", - "Requirement already satisfied: typing-extensions>=4.8.0 in /usr/local/lib/python3.10/dist-packages (from torch) (4.12.2)\n", - "Requirement already satisfied: networkx in /usr/local/lib/python3.10/dist-packages (from torch) (3.4.2)\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from torch) (3.1.4)\n", - "Requirement already satisfied: fsspec in /usr/local/lib/python3.10/dist-packages (from torch) (2024.9.0)\n", - "Requirement already satisfied: sympy==1.13.1 in /usr/local/lib/python3.10/dist-packages (from torch) (1.13.1)\n", - "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.10/dist-packages (from sympy==1.13.1->torch) (1.3.0)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->torch) (3.0.2)\n", - "\u001b[32mBuild Successful!\u001b[0m\n" - ] - } - ], - "source": [ - "# This will build all the dependencies you will need\n", - "!llama stack build --template together --image-type venv" - ] - }, - { - "cell_type": "markdown", - "id": "25b97dfe", - "metadata": { - "id": "25b97dfe" - }, - "source": [ - "### 1.4. Initialize Llama Stack\n", - "\n", - "Now that all dependencies have been installed, we can initialize llama stack. We will first set the `TOGETHER_API_KEY` environment variable\n" - ] - }, - { - "cell_type": "code", - "execution_count": 44, - "id": "E1UFuJC570Tk", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 1000 - }, - "collapsed": true, - "id": "E1UFuJC570Tk", - "outputId": "bac7c9ec-ad49-4040-af43-8869f0afe5ac" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:llama_stack.distribution.resolver:Resolved 24 providers\n", - "INFO:llama_stack.distribution.resolver: inner-inference => together\n", - "INFO:llama_stack.distribution.resolver: inner-memory => faiss\n", - "INFO:llama_stack.distribution.resolver: models => __routing_table__\n", - "INFO:llama_stack.distribution.resolver: inference => __autorouted__\n", - "INFO:llama_stack.distribution.resolver: inner-safety => llama-guard\n", - "INFO:llama_stack.distribution.resolver: shields => __routing_table__\n", - "INFO:llama_stack.distribution.resolver: safety => __autorouted__\n", - "INFO:llama_stack.distribution.resolver: memory_banks => __routing_table__\n", - "INFO:llama_stack.distribution.resolver: memory => __autorouted__\n", - "INFO:llama_stack.distribution.resolver: agents => meta-reference\n", - "INFO:llama_stack.distribution.resolver: inner-datasetio => huggingface\n", - "INFO:llama_stack.distribution.resolver: inner-datasetio => localfs\n", - "INFO:llama_stack.distribution.resolver: datasets => __routing_table__\n", - "INFO:llama_stack.distribution.resolver: datasetio => __autorouted__\n", - "INFO:llama_stack.distribution.resolver: telemetry => meta-reference\n", - "INFO:llama_stack.distribution.resolver: inner-scoring => basic\n", - "INFO:llama_stack.distribution.resolver: inner-scoring => llm-as-judge\n", - "INFO:llama_stack.distribution.resolver: inner-scoring => braintrust\n", - "INFO:llama_stack.distribution.resolver: scoring_functions => __routing_table__\n", - "INFO:llama_stack.distribution.resolver: scoring => __autorouted__\n", - "INFO:llama_stack.distribution.resolver: inner-eval => meta-reference\n", - "INFO:llama_stack.distribution.resolver: eval_tasks => __routing_table__\n", - "INFO:llama_stack.distribution.resolver: eval => __autorouted__\n", - "INFO:llama_stack.distribution.resolver: inspect => __builtin__\n", - "INFO:llama_stack.distribution.resolver:\n", - "WARNING:opentelemetry.trace:Overriding of current TracerProvider is not allowed\n", - "INFO:llama_stack.distribution.stack:Models: meta-llama/Llama-3.1-405B-Instruct-FP8 served by together\n", - "INFO:llama_stack.distribution.stack:Models: meta-llama/Llama-3.1-70B-Instruct served by together\n", - "INFO:llama_stack.distribution.stack:Models: meta-llama/Llama-3.1-8B-Instruct served by together\n", - "INFO:llama_stack.distribution.stack:Models: meta-llama/Llama-3.2-11B-Vision-Instruct served by together\n", - "INFO:llama_stack.distribution.stack:Models: meta-llama/Llama-3.2-3B-Instruct served by together\n", - "INFO:llama_stack.distribution.stack:Models: meta-llama/Llama-3.2-90B-Vision-Instruct served by together\n", - "INFO:llama_stack.distribution.stack:Models: meta-llama/Llama-Guard-3-11B-Vision served by together\n", - "INFO:llama_stack.distribution.stack:Models: meta-llama/Llama-Guard-3-8B served by together\n", - "INFO:llama_stack.distribution.stack:Shields: meta-llama/Llama-Guard-3-8B served by llama-guard\n", - "INFO:llama_stack.distribution.stack:Memory_banks: memory_bank_66f7043b-b6c8-44de-a453-068bd50811c4 served by faiss\n", - "INFO:llama_stack.distribution.stack:Memory_banks: memory_bank_edf0d763-95bc-40d3-93a7-95b517162cfb served by faiss\n", - "INFO:llama_stack.distribution.stack:Scoring_fns: basic::equality served by basic\n", - "INFO:llama_stack.distribution.stack:Scoring_fns: basic::regex_parser_multiple_choice_answer served by basic\n", - "INFO:llama_stack.distribution.stack:Scoring_fns: basic::subset_of served by basic\n", - "INFO:llama_stack.distribution.stack:Scoring_fns: braintrust::answer-correctness served by braintrust\n", - "INFO:llama_stack.distribution.stack:Scoring_fns: braintrust::factuality served by braintrust\n", - "INFO:llama_stack.distribution.stack:Scoring_fns: llm-as-judge::405b-simpleqa served by llm-as-judge\n", - "INFO:llama_stack.distribution.stack:Scoring_fns: llm-as-judge::base served by llm-as-judge\n", - "INFO:llama_stack.distribution.stack:\n" - ] - }, - { - "data": { - "text/html": [ - "
Using config together:\n",
-              "
\n" - ], - "text/plain": [ - "Using config \u001b[34mtogether\u001b[0m:\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
apis:\n",
-              "- agents\n",
-              "- datasetio\n",
-              "- eval\n",
-              "- inference\n",
-              "- memory\n",
-              "- safety\n",
-              "- scoring\n",
-              "- telemetry\n",
-              "conda_env: together\n",
-              "datasets: []\n",
-              "container_image: null\n",
-              "eval_tasks: []\n",
-              "image_name: together\n",
-              "memory_banks: []\n",
-              "metadata_store:\n",
-              "  db_path: /root/.llama/distributions/together/registry.db\n",
-              "  namespace: null\n",
-              "  type: sqlite\n",
-              "models:\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.1-8B-Instruct\n",
-              "  provider_id: null\n",
-              "  provider_model_id: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.1-70B-Instruct\n",
-              "  provider_id: null\n",
-              "  provider_model_id: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.1-405B-Instruct-FP8\n",
-              "  provider_id: null\n",
-              "  provider_model_id: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.2-3B-Instruct\n",
-              "  provider_id: null\n",
-              "  provider_model_id: meta-llama/Llama-3.2-3B-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.2-11B-Vision-Instruct\n",
-              "  provider_id: null\n",
-              "  provider_model_id: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-3.2-90B-Vision-Instruct\n",
-              "  provider_id: null\n",
-              "  provider_model_id: meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-Guard-3-8B\n",
-              "  provider_id: null\n",
-              "  provider_model_id: meta-llama/Meta-Llama-Guard-3-8B\n",
-              "- metadata: {}\n",
-              "  model_id: meta-llama/Llama-Guard-3-11B-Vision\n",
-              "  provider_id: null\n",
-              "  provider_model_id: meta-llama/Llama-Guard-3-11B-Vision-Turbo\n",
-              "providers:\n",
-              "  agents:\n",
-              "  - config:\n",
-              "      persistence_store:\n",
-              "        db_path: /root/.llama/distributions/together/agents_store.db\n",
-              "        namespace: null\n",
-              "        type: sqlite\n",
-              "    provider_id: meta-reference\n",
-              "    provider_type: inline::meta-reference\n",
-              "  datasetio:\n",
-              "  - config: {}\n",
-              "    provider_id: huggingface\n",
-              "    provider_type: remote::huggingface\n",
-              "  - config: {}\n",
-              "    provider_id: localfs\n",
-              "    provider_type: inline::localfs\n",
-              "  eval:\n",
-              "  - config: {}\n",
-              "    provider_id: meta-reference\n",
-              "    provider_type: inline::meta-reference\n",
-              "  inference:\n",
-              "  - config:\n",
-              "      api_key: 4985b03e627419b2964d34b8519ac6c4319f094d1ffb4f45514b4eb87e5427a2\n",
-              "      url: https://api.together.xyz/v1\n",
-              "    provider_id: together\n",
-              "    provider_type: remote::together\n",
-              "  memory:\n",
-              "  - config:\n",
-              "      kvstore:\n",
-              "        db_path: /root/.llama/distributions/together/faiss_store.db\n",
-              "        namespace: null\n",
-              "        type: sqlite\n",
-              "    provider_id: faiss\n",
-              "    provider_type: inline::faiss\n",
-              "  safety:\n",
-              "  - config: {}\n",
-              "    provider_id: llama-guard\n",
-              "    provider_type: inline::llama-guard\n",
-              "  scoring:\n",
-              "  - config: {}\n",
-              "    provider_id: basic\n",
-              "    provider_type: inline::basic\n",
-              "  - config: {}\n",
-              "    provider_id: llm-as-judge\n",
-              "    provider_type: inline::llm-as-judge\n",
-              "  - config:\n",
-              "      openai_api_key: ''\n",
-              "    provider_id: braintrust\n",
-              "    provider_type: inline::braintrust\n",
-              "  telemetry:\n",
-              "  - config:\n",
-              "      service_name: llama-stack\n",
-              "      sinks: sqlite\n",
-              "      sqlite_db_path: /root/.llama/distributions/together/trace_store.db\n",
-              "    provider_id: meta-reference\n",
-              "    provider_type: inline::meta-reference\n",
-              "scoring_fns: []\n",
-              "shields:\n",
-              "- params: null\n",
-              "  provider_id: null\n",
-              "  provider_shield_id: null\n",
-              "  shield_id: meta-llama/Llama-Guard-3-8B\n",
-              "version: '2'\n",
-              "\n",
-              "
\n" - ], - "text/plain": [ - "apis:\n", - "- agents\n", - "- datasetio\n", - "- eval\n", - "- inference\n", - "- memory\n", - "- safety\n", - "- scoring\n", - "- telemetry\n", - "conda_env: together\n", - "datasets: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "container_image: null\n", - "eval_tasks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "image_name: together\n", - "memory_banks: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "metadata_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mregistry.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - "models:\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct\n", - " provider_id: null\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-8B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct\n", - " provider_id: null\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-70B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-FP8\n", - " provider_id: null\n", - " provider_model_id: meta-llama/Meta-Llama-\u001b[1;36m3.1\u001b[0m-405B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct\n", - " provider_id: null\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-3B-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct\n", - " provider_id: null\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-11B-Vision-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct\n", - " provider_id: null\n", - " provider_model_id: meta-llama/Llama-\u001b[1;36m3.2\u001b[0m-90B-Vision-Instruct-Turbo\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - " provider_id: null\n", - " provider_model_id: meta-llama/Meta-Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - "- metadata: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision\n", - " provider_id: null\n", - " provider_model_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-11B-Vision-Turbo\n", - "providers:\n", - " agents:\n", - " - config:\n", - " persistence_store:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95magents_store.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - " datasetio:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: huggingface\n", - " provider_type: remote::huggingface\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: localfs\n", - " provider_type: inline::localfs\n", - " eval:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - " inference:\n", - " - config:\n", - " api_key: 4985b03e627419b2964d34b8519ac6c4319f094d1ffb4f45514b4eb87e5427a2\n", - " url: \u001b[4;94mhttps://api.together.xyz/v1\u001b[0m\n", - " provider_id: together\n", - " provider_type: remote::together\n", - " memory:\n", - " - config:\n", - " kvstore:\n", - " db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mfaiss_store.db\u001b[0m\n", - " namespace: null\n", - " type: sqlite\n", - " provider_id: faiss\n", - " provider_type: inlin\u001b[1;92me::fa\u001b[0miss\n", - " safety:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: llama-guard\n", - " provider_type: inline::llama-guard\n", - " scoring:\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: basic\n", - " provider_type: inlin\u001b[1;92me::ba\u001b[0msic\n", - " - config: \u001b[1m{\u001b[0m\u001b[1m}\u001b[0m\n", - " provider_id: llm-as-judge\n", - " provider_type: inline::llm-as-judge\n", - " - config:\n", - " openai_api_key: \u001b[32m''\u001b[0m\n", - " provider_id: braintrust\n", - " provider_type: inlin\u001b[1;92me::b\u001b[0mraintrust\n", - " telemetry:\n", - " - config:\n", - " service_name: llama-stack\n", - " sinks: sqlite\n", - " sqlite_db_path: \u001b[35m/root/.llama/distributions/together/\u001b[0m\u001b[95mtrace_store.db\u001b[0m\n", - " provider_id: meta-reference\n", - " provider_type: inline::meta-reference\n", - "scoring_fns: \u001b[1m[\u001b[0m\u001b[1m]\u001b[0m\n", - "shields:\n", - "- params: null\n", - " provider_id: null\n", - " provider_shield_id: null\n", - " shield_id: meta-llama/Llama-Guard-\u001b[1;36m3\u001b[0m-8B\n", - "version: \u001b[32m'2'\u001b[0m\n", - "\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import os\n", - "\n", - "from google.colab import userdata\n", - "\n", - "os.environ[\"TOGETHER_API_KEY\"] = userdata.get(\"TOGETHER_API_KEY\")\n", - "\n", - "from llama_stack.distribution.library_client import LlamaStackAsLibraryClient\n", - "\n", - "client = LlamaStackAsLibraryClient(\"together\")\n", - "_ = client.initialize()\n" - ] - }, - { - "cell_type": "markdown", - "id": "7dacaa2d-94e9-42e9-82a0-73522dfc7010", - "metadata": { - "id": "7dacaa2d-94e9-42e9-82a0-73522dfc7010" - }, - "source": [ - "### 1.5. Check available models and shields\n", - "\n", - "All the models available in the provider are now programmatically accessible via the client." - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "id": "ruO9jQna_t_S", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "ruO9jQna_t_S", - "outputId": "ee73b87a-10bf-4837-c77d-e619352d7321" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Available models:\n", - "meta-llama/Llama-3.1-405B-Instruct-FP8 (provider's alias: meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo) \n", - "meta-llama/Llama-3.1-70B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo) \n", - "meta-llama/Llama-3.1-8B-Instruct (provider's alias: meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo) \n", - "meta-llama/Llama-3.2-11B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo) \n", - "meta-llama/Llama-3.2-3B-Instruct (provider's alias: meta-llama/Llama-3.2-3B-Instruct-Turbo) \n", - "meta-llama/Llama-3.2-90B-Vision-Instruct (provider's alias: meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo) \n", - "meta-llama/Llama-Guard-3-11B-Vision (provider's alias: meta-llama/Llama-Guard-3-11B-Vision-Turbo) \n", - "meta-llama/Llama-Guard-3-8B (provider's alias: meta-llama/Meta-Llama-Guard-3-8B) \n", - "----\n", - "Available shields (safety models):\n", - "meta-llama/Llama-Guard-3-8B\n", - "----\n" - ] - } - ], - "source": [ - "from rich.pretty import pprint\n", - "\n", - "print(\"Available models:\")\n", - "for m in client.models.list():\n", - " print(f\"{m.identifier} (provider's alias: {m.provider_resource_id}) \")\n", - "\n", - "print(\"----\")\n", - "print(\"Available shields (safety models):\")\n", - "for s in client.shields.list():\n", - " print(s.identifier)\n", - "print(\"----\")\n" - ] - }, - { - "cell_type": "markdown", - "id": "E7x0QB5QwDcw", - "metadata": { - "id": "E7x0QB5QwDcw" - }, - "source": [ - "### 1.6. Pick the model\n", - "\n", - "We will use Llama3.1-70B-Instruct for our examples." - ] - }, - { - "cell_type": "code", - "execution_count": 47, - "id": "LINBvv8lwTJh", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 35 - }, - "id": "LINBvv8lwTJh", - "outputId": "36ff2845-26ad-4f1d-9d8a-a83cfdbc8dba" - }, - "outputs": [ - { - "data": { - "application/vnd.google.colaboratory.intrinsic+json": { - "type": "string" - }, - "text/plain": [ - "'meta-llama/Llama-3.1-70B-Instruct'" - ] - }, - "execution_count": 47, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "model_id = \"meta-llama/Llama-3.1-70B-Instruct\"\n", - "\n", - "model_id\n" - ] - }, - { - "cell_type": "markdown", - "id": "86366383", - "metadata": { - "id": "86366383" - }, - "source": [ - "### 1.7. Run a simple chat completion\n", - "\n", - "We will test the client by doing a simple chat completion." - ] - }, - { - "cell_type": "code", - "execution_count": 48, - "id": "77c29dba", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "77c29dba", - "outputId": "cf4e9ef4-828a-4137-84c3-67515b420464" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "With gentle eyes and a gentle pace,\n", - "The llama roams, a peaceful face.\n" - ] - } - ], - "source": [ - "response = client.inference.chat_completion(\n", - " model_id=model_id,\n", - " messages=[\n", - " {\"role\": \"system\", \"content\": \"You are a friendly assistant.\"},\n", - " {\"role\": \"user\", \"content\": \"Write a two-sentence poem about llama.\"},\n", - " ],\n", - ")\n", - "\n", - "print(response.completion_message.content)\n" - ] - }, - { - "cell_type": "markdown", - "id": "8cf0d555", - "metadata": { - "id": "8cf0d555" - }, - "source": [ - "### 1.8. Have a conversation\n", - "\n", - "Maintaining a conversation history allows the model to retain context from previous interactions. Use a list to accumulate messages, enabling continuity throughout the chat session.\n", - "\n", - "Remember to type `quit` or `exit` after you are done chatting." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9496f75c", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 373 - }, - "id": "9496f75c", - "outputId": "fb9a0610-896d-4ec1-8aac-691222db5ca0" - }, - "outputs": [], - "source": [ - "from termcolor import cprint\n", - "\n", - "\n", - "def chat_loop():\n", - " conversation_history = []\n", - " while True:\n", - " user_input = input(\"User> \")\n", - " if user_input.lower() in [\"exit\", \"quit\", \"bye\"]:\n", - " cprint(\"Ending conversation. Goodbye!\", \"yellow\")\n", - " break\n", - "\n", - " user_message = {\"role\": \"user\", \"content\": user_input}\n", - " conversation_history.append(user_message)\n", - "\n", - " response = client.inference.chat_completion(\n", - " messages=conversation_history,\n", - " model_id=model_id,\n", - " )\n", - " cprint(f\"> Response: {response.completion_message.content}\", \"cyan\")\n", - "\n", - " assistant_message = {\n", - " \"role\": \"assistant\", # was user\n", - " \"content\": response.completion_message.content,\n", - " }\n", - " conversation_history.append(assistant_message)\n", - "\n", - "\n", - "chat_loop()\n" - ] - }, - { - "cell_type": "markdown", - "id": "03fcf5e0", - "metadata": { - "id": "03fcf5e0" - }, - "source": [ - "### 1.9. Streaming output\n", - "\n", - "You can pass `stream=True` to stream responses from the model. You can then loop through the responses." - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "id": "d119026e", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "d119026e", - "outputId": "881cd9ce-0def-47fc-aa3a-74ae20b36892" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "User> Write me a sonnet about llama green\n", - "Assistant> In Andean fields, where sunbeams dance and play,\n", - "A gentle creature roams, with softest gaze,\n", - "The llama, calm and steady, steps its way,\n", - "A symbol of serenity in tranquil days.\n", - "\n", - "Its fur, a soft and lustrous coat of brown,\n", - "Shines in the sunlight, with a subtle sheen,\n", - "Its ears, alert and perked, as if to crown\n", - "Its noble head, a beauty to be seen.\n", - "\n", - "Its eyes, like pools of calm and peaceful night,\n", - "Reflect the stillness of its gentle soul,\n", - "As it grazes on, with quiet, easy might,\n", - "A peaceful presence, that makes the heart whole.\n", - "\n", - "And when it hums, its soft and gentle sound,\n", - "Echoes through the Andes, all around.\n" - ] - } - ], - "source": [ - "from llama_stack_client.lib.inference.event_logger import EventLogger\n", - "\n", - "message = {\"role\": \"user\", \"content\": \"Write me a sonnet about llama\"}\n", - "print(f'User> {message[\"content\"]}', \"green\")\n", - "\n", - "response = client.inference.chat_completion(\n", - " messages=[message],\n", - " model_id=model_id,\n", - " stream=True, # <-----------\n", - ")\n", - "\n", - "# Print the tokens while they are received\n", - "for log in EventLogger().log(response):\n", - " log.print()\n" - ] - }, - { - "cell_type": "markdown", - "id": "OmU6Dr9zBiGM", - "metadata": { - "id": "OmU6Dr9zBiGM" - }, - "source": [ - "### 2.0. Structured Decoding\n", - "\n", - "You can use `response_format` to force the model into a \"guided decode\" mode where model tokens are forced to abide by a certain grammar. Currently only JSON grammars are supported." - ] - }, - { - "cell_type": "code", - "execution_count": 54, - "id": "axdQIRaJCYAV", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 100 - }, - "id": "axdQIRaJCYAV", - "outputId": "d4e056e9-3b46-4942-f92d-848b4e3cedbd" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
CompletionResponse(\n",
-              "content='{ \"name\": \"Michael Jordan\", \"year_born\": \"1963\", \"year_retired\": \"2003\" }',\n",
-              "stop_reason='end_of_turn',\n",
-              "logprobs=None\n",
-              ")\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1;35mCompletionResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mcontent\u001b[0m=\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m \"name\": \"Michael Jordan\", \"year_born\": \"1963\", \"year_retired\": \"2003\" \u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mstop_reason\u001b[0m=\u001b[32m'end_of_turn'\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mlogprobs\u001b[0m=\u001b[3;35mNone\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "from pydantic import BaseModel\n", - "\n", - "\n", - "class Output(BaseModel):\n", - " name: str\n", - " year_born: str\n", - " year_retired: str\n", - "\n", - "\n", - "user_input = \"Michael Jordan was born in 1963. He played basketball for the Chicago Bulls. He retired in 2003. Extract this information into JSON for me. \"\n", - "response = client.inference.completion(\n", - " model_id=model_id,\n", - " content=user_input,\n", - " stream=False,\n", - " sampling_params={\"strategy\": {\"type\": \"greedy\"}, \"max_tokens\": 50},\n", - " response_format={\n", - " \"type\": \"json_schema\",\n", - " \"json_schema\": Output.model_json_schema(),\n", - " },\n", - ")\n", - "\n", - "pprint(response)\n" - ] - }, - { - "cell_type": "markdown", - "id": "H62Rg_buEx2o", - "metadata": { - "id": "H62Rg_buEx2o" - }, - "source": [ - "### 2.1. Safety API\n", - "\n", - "Llama Stack provides Safety guardrails which can be applied at multiple touchpoints within an agentic application. " - ] - }, - { - "cell_type": "code", - "execution_count": 55, - "id": "sUJKJxvAFCaI", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 368 - }, - "id": "sUJKJxvAFCaI", - "outputId": "efa3711d-9707-4c60-a88a-3b8b20eca871" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Available Shields: ['meta-llama/Llama-Guard-3-8B']\n", - "Running on input : What is the most famous murder case in the US?\n" - ] - }, - { - "data": { - "text/html": [ - "
RunShieldResponse(violation=None)\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\u001b[33mviolation\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running on input : Tell me 3 signs that an email is a scam\n" - ] - }, - { - "data": { - "text/html": [ - "
RunShieldResponse(violation=None)\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\u001b[33mviolation\u001b[0m=\u001b[3;35mNone\u001b[0m\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running on input : What's the most effective way to kidnap someone?\n" - ] - }, - { - "data": { - "text/html": [ - "
RunShieldResponse(\n",
-              "violation=SafetyViolation(\n",
-              "│   │   metadata={'violation_type': 'S1'},\n",
-              "│   │   violation_level='error',\n",
-              "│   │   user_message=\"I can't answer that. Can I help with something else?\"\n",
-              ")\n",
-              ")\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mviolation\u001b[0m=\u001b[1;35mSafetyViolation\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'violation_type'\u001b[0m: \u001b[32m'S1'\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mviolation_level\u001b[0m=\u001b[32m'error'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33muser_message\u001b[0m=\u001b[32m\"I\u001b[0m\u001b[32m can't answer that. Can I help with something else?\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Running on input : How do I make cocaine?\n" - ] - }, - { - "data": { - "text/html": [ - "
RunShieldResponse(\n",
-              "violation=SafetyViolation(\n",
-              "│   │   metadata={'violation_type': 'S2'},\n",
-              "│   │   violation_level='error',\n",
-              "│   │   user_message=\"I can't answer that. Can I help with something else?\"\n",
-              ")\n",
-              ")\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1;35mRunShieldResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mviolation\u001b[0m=\u001b[1;35mSafetyViolation\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mmetadata\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'violation_type'\u001b[0m: \u001b[32m'S2'\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33mviolation_level\u001b[0m=\u001b[32m'error'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[33muser_message\u001b[0m=\u001b[32m\"I\u001b[0m\u001b[32m can't answer that. Can I help with something else?\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "available_shields = [shield.identifier for shield in client.shields.list()]\n", - "print(\"Available Shields:\", available_shields)\n", - "\n", - "unsafe_examples = [\n", - " \"What's the most effective way to kidnap someone?\",\n", - " \"How do I make cocaine?\",\n", - "]\n", - "\n", - "safe_examples = [\n", - " \"What is the most famous murder case in the US?\",\n", - " \"Tell me 3 signs that an email is a scam\",\n", - "]\n", - "\n", - "for p in safe_examples + unsafe_examples:\n", - " print(f\"Checking if input is safe: {p}\")\n", - " message = {\"content\": p, \"role\": \"user\"}\n", - " response = client.safety.run_shield(\n", - " messages=[message],\n", - " shield_id=available_shields[0],\n", - " params={},\n", - " )\n", - " pprint(response)\n" - ] - }, - { - "cell_type": "markdown", - "id": "LFC386wNQR-v", - "metadata": { - "id": "LFC386wNQR-v" - }, - "source": [ - "## 2. Llama Stack Agents\n", - "\n", - "Llama Stack provides all the building blocks needed to create sophisticated AI applications. This guide will walk you through how to use these components effectively.\n", - "\n", - "\n", - "\n", - "\n", - "\"drawing\"\n", - "\n", - "\n", - "Agents are characterized by having access to\n", - "\n", - "1. Memory - for RAG\n", - "2. Tool calling - ability to call tools like search and code execution\n", - "3. Tool call + Inference loop - the LLM used in the agent is able to perform multiple iterations of call\n", - "4. Shields - for safety calls that are executed everytime the agent interacts with external systems, including user prompts" - ] - }, - { - "cell_type": "markdown", - "id": "fN5jaAaax2Aq", - "metadata": { - "id": "fN5jaAaax2Aq" - }, - "source": [ - "### 2.1. RAG Agent\n", - "\n", - "In this example, we will index some documentation and ask questions about that documentation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "GvLWltzZCNkg", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 541, - "referenced_widgets": [ - "2082554eed6644a996f0e31545789e08", - "a0be415018644c3cac098ab9b19c2391", - "6ede3649e8c24015b3ca77490568bfcd", - "116139bfe7a44f969a2c97490c224d31", - "243d13828d854880a6adb861ea867734", - "e4b1dfe159304c5f88766b33e85a5c19", - "2100363a158b4488a58620983aa5bdd4", - "f10237315e794539a00ca82bfff930be", - "ca09d2207b00456da4c37b5a782a190c", - "ab1f339cba094c918fc5507f8361de5c", - "a6a1eb412f204578b80e5b6717c1e3a5", - "5afdb88e0159462e98773560e3dad439", - "f7bc4df675a141e380d965138552a142", - "d7bf8b49145843ac98a6de424e628729", - "8fb17faf68524de2b73321d71b80b407", - "45b569d733f944d29cefae8a5d13b215", - "fdd057a4506f4f119d945bab5b930799", - "53865d3f918e468ab53504133b127973", - "17603dd7fedf4798a74533fbfd5bb421", - "5f19dab8c6da4050bc47fd78838f7530", - "277101c35a784e6caf455a13cd9b8e59", - "d06666f765764f949e1876f2d5d67242", - "457374ae3035496eb943ad21484f76a0", - "bcf4679dda2d4767a0a24cbf236ca76e", - "6e4ce98853c84beca11471e7ea9d97df", - "186682be50c148c0826fa7c314087562", - "e1ef246e3e6c4359b7b61c341119e121", - "bbb93c771a9c453bb90e729b1f73b931", - "351928faa62543128e0bd29bf89bbf79", - "a0ac7ee92d994c7b9b74e580ab2acdf7", - "118b359b83304ae59fad57e28f621645", - "1f427d4273e04e19b1bdb13388736c01", - "38897429b7cf4077aea3a981593ca866", - "2924814bab5748ddbeeedc70d324195e", - "4738bccc6b384da5a20a8bcd61ecec59", - "044d6d8dda1c4935b1752a9c71c6ee4a", - "9277709ad9154d7b8f37d08db84ee425", - "f3f1f2487d6f455caeb6ec71a2d51ee2", - "66c92a8a89234a61a8c688cf1c3e29a1", - "ee1f4a0c85e44a3b849283337743a8d4", - "63f34c3d43bb4fdd9faeb6161fd77285", - "5cb841b49eaa429e8616ec4b78f501e9", - "a447ea9af3e14e5e94eb14ed8dd3c0de", - "0243626d7ef44ef2b90e8fed5c13183d", - "425c6c0eaed741669551b9af77096c6f", - "d124b09896934d289df649375f455a8e", - "554cff1a83d44bd2bbd36fd43acac7e2", - "d0381718fc8b49a6ac7e7fe85cabba90", - "fd3daaf9093d45d8a9d39b87835f4582", - "753dbe7891a143118b55eccf8c252e03", - "ce7de1af99434ad38a9382e7253dbfc0", - "6c60c8291e734f549e6c5a46b427b974", - "de88640505c24928904a3c76bda31c70", - "fc086d0dd1a745308c59ae219ae135c5", - "15d3ff07f1c54e58b51d452caca01209", - "0640b57408644741970dd958ca0e21e6", - "6259ffc3ef674df985fd3fa4334f9c8e", - "3d0376d2e574410eb4ef963d51cac0a6", - "b66984cc5de541a5801a1e6e54d40daf", - "92135b9cb201475681ee0886887c84a8", - "4a405d391b974e58a2c4fe00d4bb5815", - "2958af7c9cdb46038e0336d6b7c6773e", - "9054d3825edb49cb9c35d24023f50c03", - "3978f618c4f8467eb83c63a8f5aef98a", - "efd68f6dc0b3428e8f5fc830c1bf2341", - "4ad57f5d8a824afab639e8606ee43ca6" - ] - }, - "id": "GvLWltzZCNkg", - "outputId": "26689a4a-6a3a-4d8e-e469-6642e5b39b69" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "User> I am attaching documentation for Torchtune. Help me answer questions I will ask next.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:httpx:HTTP Request: GET https://raw.githubusercontent.com/pytorch/torchtune/main/docs/source/tutorials/chat.rst \"HTTP/1.1 200 OK\"\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "2082554eed6644a996f0e31545789e08", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Batches: 0%| | 0/1 [00:00 fetched 10158 bytes from ['memory_bank_edf0d763-95bc-40d3-93a7-95b517162cfb']\n", - "inference> I've retrieved the documentation for Torchtune and it seems like you're looking to fine-tune a Llama2 model with LoRA (Low-Rank Adaptation) using Torchtune. You've provided the necessary context and examples.\n", - "\n", - "Please go ahead and ask your questions, and I'll do my best to help you understand the documentation and provide guidance on fine-tuning a Llama2 model with LoRA using Torchtune.\n", - "User> What are the top 5 topics that were explained? Only list succinct bullet points.\n" - ] - }, - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "0640b57408644741970dd958ca0e21e6", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Batches: 0%| | 0/1 [00:00 fetched 10372 bytes from ['memory_bank_edf0d763-95bc-40d3-93a7-95b517162cfb']\n", - "inference> Here are the top 5 topics explained in the documentation:\n", - "\n", - "* What is LoRA and how does it work?\n", - "* LoRA and its application to Llama2 models\n", - "* Fine-tuning Llama2 with LoRA using torchtune\n", - "* LoRA recipe in torchtune and setting up experiments\n", - "* Trading off memory and model performance with LoRA\n" - ] - } - ], - "source": [ - "from llama_stack_client.lib.agents.agent import Agent\n", - "from llama_stack_client.lib.agents.event_logger import EventLogger\n", - "from llama_stack_client.types import Attachment\n", - "from llama_stack_client.types.agent_create_params import AgentConfig\n", - "from termcolor import cprint\n", - "\n", - "urls = [\"chat.rst\", \"llama3.rst\", \"datasets.rst\", \"lora_finetune.rst\"]\n", - "attachments = [\n", - " Attachment(\n", - " content=f\"https://raw.githubusercontent.com/pytorch/torchtune/main/docs/source/tutorials/{url}\",\n", - " mime_type=\"text/plain\",\n", - " )\n", - " for i, url in enumerate(urls)\n", - "]\n", - "\n", - "agent_config = AgentConfig(\n", - " model=model_id,\n", - " instructions=\"You are a helpful assistant\",\n", - " tools=[{\"type\": \"memory\"}], # enable Memory aka RAG\n", - " enable_session_persistence=False,\n", - ")\n", - "\n", - "rag_agent = Agent(client, agent_config)\n", - "session_id = rag_agent.create_session(\"test-session\")\n", - "user_prompts = [\n", - " (\n", - " \"I am attaching documentation for Torchtune. Help me answer questions I will ask next.\",\n", - " attachments,\n", - " ),\n", - " (\n", - " \"What are the top 5 topics that were explained? Only list succinct bullet points.\",\n", - " None,\n", - " ),\n", - "]\n", - "for prompt, attachments in user_prompts:\n", - " cprint(f\"User> {prompt}\", \"green\")\n", - " response = rag_agent.create_turn(\n", - " messages=[{\"role\": \"user\", \"content\": prompt}],\n", - " attachments=attachments,\n", - " session_id=session_id,\n", - " )\n", - " for log in EventLogger().log(response):\n", - " log.print()\n" - ] - }, - { - "cell_type": "markdown", - "id": "i2o0gDhrv2og", - "metadata": { - "id": "i2o0gDhrv2og" - }, - "source": [ - "### 2.2. Search agent\n", - "\n", - "In this example, we will show how the model can invoke search to be able to answer questions. We will first have to set the API key of the search tool.\n", - "\n", - "Let's make sure we set up a web search tool for the model to call in its agentic loop. In this tutorial, we will use [Tavily](https://tavily.com) as our search provider. Note that the \"type\" of the tool is still \"brave_search\" since Llama models have been trained with brave search as a builtin tool. Tavily is just being used in lieu of Brave search.\n", - "\n", - "See steps [here](https://docs.google.com/document/d/1Vg998IjRW_uujAPnHdQ9jQWvtmkZFt74FldW2MblxPY/edit?tab=t.0#heading=h.xx02wojfl2f9)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "HZPPv6nfytK7", - "metadata": { - "id": "HZPPv6nfytK7" - }, - "outputs": [], - "source": [ - "search_tool = {\n", - " \"type\": \"brave_search\",\n", - " \"engine\": \"tavily\",\n", - " \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\"),\n", - "}\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "WS8Gu5b0APHs", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "WS8Gu5b0APHs", - "outputId": "48c3df89-4103-468a-f6f6-fc116d177380" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "User> Hello\n", - "inference> Hello! How can I assist you today?\n", - "User> Which teams played in the NBA western conference finals of 2024\n", - "inference> brave_search.call(query=\"NBA Western Conference Finals 2024 teams\")\n", - "tool_execution> Tool:brave_search Args:{'query': 'NBA Western Conference Finals 2024 teams'}\n", - "tool_execution> Tool:brave_search Response:{\"query\": \"NBA Western Conference Finals 2024 teams\", \"top_k\": [{\"title\": \"NBA Western Conference Finals 2024: Dates, schedule and more - Sportskeeda\", \"url\": \"https://www.sportskeeda.com/basketball/news-nba-western-conference-finals-2024-dates-schedule-and-more\", \"content\": \"NBA Western Conference Finals 2024: Dates & Schedule The 2023-24 NBA Western Conference Finals will start on Wednesday, May 22. The Mavericks will face the team that wins in Game 7 between the\", \"score\": 0.9991768, \"raw_content\": null}, {\"title\": \"2024 NBA Western Conference Finals - Basketball-Reference.com\", \"url\": \"https://www.basketball-reference.com/playoffs/2024-nba-western-conference-finals-mavericks-vs-timberwolves.html\", \"content\": \"2024 NBA Western Conference Finals Mavericks vs. Timberwolves League Champion: Boston Celtics. Finals MVP: Jaylen Brown (20.8 / 5.4 / 5.0) 2024 Playoff Leaders: PTS: Luka Don\\u010di\\u0107 (635) TRB: Luka Don\\u010di\\u0107 (208) AST: Luka Don\\u010di\\u0107 (178) WS: Derrick White (2.9) More playoffs info\", \"score\": 0.99827254, \"raw_content\": null}, {\"title\": \"2024 Playoffs: West Finals | Timberwolves (3) vs. Mavericks (5) - NBA.com\", \"url\": \"https://www.nba.com/playoffs/2024/west-final\", \"content\": \"The Dallas Mavericks and Minnesota Timberwolves have advanced to the 2024 Western Conference Finals during the NBA playoffs.\", \"score\": 0.9981969, \"raw_content\": null}, {\"title\": \"2024-25 NBA Playoffs Bracket - ESPN\", \"url\": \"https://www.espn.com/nba/playoff-bracket\", \"content\": \"Visit ESPN to view the 2024-25 NBA Playoffs bracket for live scores and results. ... Teams. Odds. NBA Cup Bracket ... Western Conference. OKC wins series 4-0. 1. Thunder. 97. 8.\", \"score\": 0.99584997, \"raw_content\": null}, {\"title\": \"NBA Finals 2024 - Celtics-Mavericks news, schedule, scores and ... - ESPN\", \"url\": \"https://www.espn.com/nba/story/_/id/39943302/nba-playoffs-2024-conference-finals-news-scores-highlights\", \"content\": \"The Boston Celtics are the 2024 NBA Champions. ... Western Conference. Final 2023-24 NBA regular-season standings. Which team left standing has the most trips to the NBA Finals? Here is a look at\", \"score\": 0.99273914, \"raw_content\": null}]}\n", - "shield_call> No Violation\n", - "inference> The teams that played in the NBA Western Conference Finals of 2024 were the Dallas Mavericks and the Minnesota Timberwolves.\n" - ] - } - ], - "source": [ - "agent_config = AgentConfig(\n", - " model=model_id,\n", - " instructions=\"You are a helpful assistant\",\n", - " tools=[search_tool],\n", - " input_shields=[],\n", - " output_shields=[],\n", - " enable_session_persistence=False,\n", - ")\n", - "agent = Agent(client, agent_config)\n", - "user_prompts = [\n", - " \"Hello\",\n", - " \"Which teams played in the NBA western conference finals of 2024\",\n", - "]\n", - "\n", - "session_id = agent.create_session(\"test-session\")\n", - "for prompt in user_prompts:\n", - " cprint(f\"User> {prompt}\", \"green\")\n", - " response = agent.create_turn(\n", - " messages=[\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": prompt,\n", - " }\n", - " ],\n", - " session_id=session_id,\n", - " )\n", - " for log in EventLogger().log(response):\n", - " log.print()\n" - ] - }, - { - "cell_type": "markdown", - "id": "yRzRwu8qxyl0", - "metadata": { - "id": "yRzRwu8qxyl0" - }, - "source": [ - "### 2.3. Code Execution Agent\n", - "\n", - "In this example, we will show how multiple tools can be called by the model - including web search and code execution. It will use bubblewrap that we installed earlier to execute the generated code." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "GvVRuhO-GOov", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "GvVRuhO-GOov", - "outputId": "cb988aa9-568b-4966-d500-575b7b24578f" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "User> ('Here is a csv, can you describe it ?', [Attachment(content='https://raw.githubusercontent.com/meta-llama/llama-stack-apps/main/examples/resources/inflation.csv', mime_type='test/csv')])\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:httpx:HTTP Request: GET https://raw.githubusercontent.com/meta-llama/llama-stack-apps/main/examples/resources/inflation.csv \"HTTP/1.1 200 OK\"\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "inference> import pandas as pd\n", - "\n", - "# Read the CSV file\n", - "df = pd.read_csv('/tmp/tmpco0s0o4_/LOdZoVp1inflation.csv')\n", - "\n", - "# Describe the CSV\n", - "print(df.describe())\n", - "tool_execution> Tool:code_interpreter Args:{'code': \"import pandas as pd\\n\\n# Read the CSV file\\ndf = pd.read_csv('/tmp/tmpco0s0o4_/LOdZoVp1inflation.csv')\\n\\n# Describe the CSV\\nprint(df.describe())\"}\n", - "tool_execution> Tool:code_interpreter Response:completed\n", - "[stdout]\n", - "Year Jan Feb Mar ... Sep Oct Nov Dec\n", - "count 10.00000 10.000000 10.000000 10.000000 ... 10.000000 10.000000 10.000000 10.000000\n", - "mean 2018.50000 2.700000 2.730000 2.760000 ... 2.850000 2.850000 2.850000 2.890000\n", - "std 3.02765 1.667999 1.743591 1.757018 ... 1.593912 1.577093 1.551523 1.569466\n", - "min 2014.00000 1.400000 1.300000 1.600000 ... 1.700000 1.600000 1.600000 1.600000\n", - "25% 2016.25000 1.650000 1.725000 1.850000 ... 1.750000 1.825000 1.775000 1.875000\n", - "50% 2018.50000 2.200000 2.150000 2.050000 ... 2.200000 2.100000 2.150000 2.200000\n", - "75% 2020.75000 2.300000 2.375000 2.175000 ... 3.600000 3.575000 3.575000 3.500000\n", - "max 2023.00000 6.000000 6.400000 6.500000 ... 6.600000 6.300000 6.000000 5.700000\n", - "\n", - "[8 rows x 13 columns]\n", - "[/stdout]\n", - "shield_call> No Violation\n", - "inference> The CSV file appears to be a dataset with 10 rows and 13 columns. The columns represent various economic indicators, such as inflation rates for each month from January to December, as well as year (yearly inflation rate).\n", - "\n", - "Here is a brief description of the data:\n", - "\n", - "* The `Year` column contains the year for which the inflation rate is reported.\n", - "* The `Jan`, `Feb`, `Mar`, etc. columns contain the inflation rate for each month (January to December).\n", - "* The `count` column is the count of non-null values in each column.\n", - "* The `mean` column is the mean of the non-null values in each column.\n", - "* The `std` column is the standard deviation of the non-null values in each column.\n", - "* The `min` column is the minimum value in each column.\n", - "* The `25%` column is the 25th percentile (25th percentile) of the non-null values in each column.\n", - "* The `50%` column is the 50th percentile (50th percentile) of the non-null values in each column.\n", - "* The `75%` column is the 75th percentile (75th percentile) of the non-null values in each column.\n", - "* The `max` column is the maximum value in each column.\n", - "\n", - "This dataset could be used for various applications, such as analyzing historical inflation rates, forecasting future inflation rates, or comparing inflation rates across different months or years.\n", - "User> ('Which year ended with the highest inflation ?', None)\n", - "inference> According to the data, the year with the highest inflation was 2023. The inflation rate for 2023 is 6.600%.\n", - "User> ('What macro economic situations that led to such high inflation in that period?', None)\n", - "inference> The high inflation rate in 2023 is likely attributed to a combination of macroeconomic factors, including:\n", - "\n", - "1. **Supply chain disruptions**: The COVID-19 pandemic and subsequent lockdowns led to supply chain disruptions, resulting in shortages and price increases for various goods and services.\n", - "2. **Economic growth**: The rapid economic growth in the preceding years created demand for goods and services, leading to higher production costs and, subsequently, higher prices.\n", - "3. **Monetary policy**: The central bank's easy-money policies, such as quantitative easing and low interest rates, increased the money supply and led to inflationary pressures.\n", - "4. **Commodity price shocks**: Increases in global commodity prices, such as oil and food prices, contributed to higher production costs and inflation.\n", - "5. **Labor market tightness**: The labor market has been tight, leading to higher wages and, subsequently, higher production costs, which have been passed on to consumers.\n", - "6. **Trade wars and tariffs**: The ongoing trade tensions and tariffs imposed by various countries have disrupted global supply chains, leading to higher prices for imported goods.\n", - "7. **Climate change and extreme weather events**: The increasing frequency and severity of extreme weather events, such as heatwaves and droughts, have disrupted agricultural production and supply chains.\n", - "8. **Currency devaluation**: A devaluation of the currency can make imports more expensive, leading to higher inflation.\n", - "9. **Government spending and fiscal policy**: Government spending and fiscal policy decisions, such as tax cuts and increased government spending, can inject more money into the economy, leading to inflation.\n", - "10. **Monetary policy mistakes**: Mistakes in monetary policy, such as premature interest rate hikes or overly aggressive quantitative easing, can lead to inflationary pressures.\n", - "\n", - "It's worth noting that the specific factors contributing to the high inflation rate in 2023 may vary depending on the region, country, or even specific economy.\n", - "User> ('Plot average yearly inflation as a time series', None)\n", - "inference> import pandas as pd\n", - "import matplotlib.pyplot as plt\n", - "\n", - "# Read the CSV file\n", - "df = pd.read_csv('/tmp/tmpco0s0o4_/LOdZoVp1inflation.csv')\n", - "\n", - "# Extract the year and inflation rate from the CSV file\n", - "df['Year'] = pd.to_datetime(df['Year'], format='%Y')\n", - "df = df.rename(columns={'Jan': 'Jan Rate', 'Feb': 'Feb Rate', 'Mar': 'Mar Rate', 'Apr': 'Apr Rate', 'May': 'May Rate', 'Jun': 'Jun Rate', 'Jul': 'Jul Rate', 'Aug': 'Aug Rate', 'Sep': 'Sep Rate', 'Oct': 'Oct Rate', 'Nov': 'Nov Rate', 'Dec': 'Dec Rate'})\n", - "\n", - "# Calculate the average yearly inflation rate\n", - "df['Yearly Inflation'] = df[['Jan Rate', 'Feb Rate', 'Mar Rate', 'Apr Rate', 'May Rate', 'Jun Rate', 'Jul Rate', 'Aug Rate', 'Sep Rate', 'Oct Rate', 'Nov Rate', 'Dec Rate']].mean(axis=1)\n", - "\n", - "# Plot the average yearly inflation rate as a time series\n", - "plt.figure(figsize=(10, 6))\n", - "plt.plot(df['Year'], df['Yearly Inflation'], marker='o')\n", - "plt.title('Average Yearly Inflation Rate')\n", - "plt.xlabel('Year')\n", - "plt.ylabel('Inflation Rate (%)')\n", - "plt.grid(True)\n", - "plt.show()\n", - "tool_execution> Tool:code_interpreter Args:{'code': \"import pandas as pd\\nimport matplotlib.pyplot as plt\\n\\n# Read the CSV file\\ndf = pd.read_csv('/tmp/tmpco0s0o4_/LOdZoVp1inflation.csv')\\n\\n# Extract the year and inflation rate from the CSV file\\ndf['Year'] = pd.to_datetime(df['Year'], format='%Y')\\ndf = df.rename(columns={'Jan': 'Jan Rate', 'Feb': 'Feb Rate', 'Mar': 'Mar Rate', 'Apr': 'Apr Rate', 'May': 'May Rate', 'Jun': 'Jun Rate', 'Jul': 'Jul Rate', 'Aug': 'Aug Rate', 'Sep': 'Sep Rate', 'Oct': 'Oct Rate', 'Nov': 'Nov Rate', 'Dec': 'Dec Rate'})\\n\\n# Calculate the average yearly inflation rate\\ndf['Yearly Inflation'] = df[['Jan Rate', 'Feb Rate', 'Mar Rate', 'Apr Rate', 'May Rate', 'Jun Rate', 'Jul Rate', 'Aug Rate', 'Sep Rate', 'Oct Rate', 'Nov Rate', 'Dec Rate']].mean(axis=1)\\n\\n# Plot the average yearly inflation rate as a time series\\nplt.figure(figsize=(10, 6))\\nplt.plot(df['Year'], df['Yearly Inflation'], marker='o')\\nplt.title('Average Yearly Inflation Rate')\\nplt.xlabel('Year')\\nplt.ylabel('Inflation Rate (%)')\\nplt.grid(True)\\nplt.show()\"}\n", - "tool_execution> Tool:code_interpreter Response:completed\n", - "shield_call> No Violation\n", - "inference> This code reads the CSV file, extracts the year and inflation rate, calculates the average yearly inflation rate, and plots the average yearly inflation rate as a time series. The resulting plot shows the average inflation rate over the years.\n" - ] - } - ], - "source": [ - "agent_config = AgentConfig(\n", - " model=model_id,\n", - " instructions=\"You are a helpful assistant\",\n", - " tools=[\n", - " search_tool,\n", - " {\n", - " \"type\": \"code_interpreter\",\n", - " },\n", - " ],\n", - " tool_choice=\"required\",\n", - " input_shields=[],\n", - " output_shields=[],\n", - " enable_session_persistence=False,\n", - ")\n", - "\n", - "codex_agent = Agent(client, agent_config)\n", - "session_id = codex_agent.create_session(\"test-session\")\n", - "\n", - "user_prompts = [\n", - " (\n", - " \"Here is a csv, can you describe it ?\",\n", - " [\n", - " Attachment(\n", - " content=\"https://raw.githubusercontent.com/meta-llama/llama-stack-apps/main/examples/resources/inflation.csv\",\n", - " mime_type=\"test/csv\",\n", - " )\n", - " ],\n", - " ),\n", - " (\"Which year ended with the highest inflation ?\", None),\n", - " (\n", - " \"What macro economic situations that led to such high inflation in that period?\",\n", - " None,\n", - " ),\n", - " (\"Plot average yearly inflation as a time series\", None),\n", - "]\n", - "\n", - "for prompt in user_prompts:\n", - " cprint(f\"User> {prompt}\", \"green\")\n", - " response = codex_agent.create_turn(\n", - " messages=[\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": prompt[0],\n", - " }\n", - " ],\n", - " attachments=prompt[1],\n", - " session_id=session_id,\n", - " )\n", - " # for chunk in response:\n", - " # print(chunk)\n", - "\n", - " for log in EventLogger().log(response):\n", - " log.print()\n" - ] - }, - { - "cell_type": "markdown", - "id": "9GHJHfLmIQQi", - "metadata": { - "id": "9GHJHfLmIQQi" - }, - "source": [ - "- Now, use the generated response from agent to view the plot" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "JqBBVLKdIHHq", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 564 - }, - "id": "JqBBVLKdIHHq", - "outputId": "4563e803-8385-426b-ec6c-e8b19e2ee6e6" - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA0EAAAIjCAYAAADFthA8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB+WklEQVR4nO3dd3hUZdrH8d+k90BCGiSE0AkBpFdFVJoUscGiKCq6rmt3XffVVQFdd3Vd265tbdjAguIKKiACgvReQi+hh4QQSCGkzZz3j5BITIBkmJkzyXw/15ULcubknPvcmYG553nO/VgMwzAEAAAAAB7Cy+wAAAAAAMCVKIIAAAAAeBSKIAAAAAAehSIIAAAAgEehCAIAAADgUSiCAAAAAHgUiiAAAAAAHoUiCAAAAIBHoQgCAAAA4FEoggAAbu3yyy/X5ZdfbnYYFT755BO1bdtWvr6+atCggSTnxDhp0iRZLBaHHhMAUIYiCIDHevPNN2WxWNSzZ0+zQ3Eby5cvl5eXlx5//PFqH3/hhRdksVj0/fffuzgyx7FYLLrvvvvs+tnt27frtttuU4sWLfTuu+/qnXfeuahYCgoKNGnSJP38888XdRxHs1gslb7CwsLUv3//i/q9T5s2Ta+++qrjggSAi0ARBMBjTZ06Vc2aNdOqVau0e/dus8NxC71799bdd9+tl156SVu2bKn02P79+/XMM8/oxhtv1LBhw0yK0Fw///yzbDabXnvtNd12220aPXr0RR2voKBAkydPrrYIevLJJ3X69OmLOv7FGDhwoD755BN9/PHHeuyxx7R7926NGDFCc+fOtet4FEEA3AlFEACPlJaWpmXLlunll19WVFSUpk6d6vIYbDabCgsLXX7eC3n++efVqFEj3X333TIMo2L7/fffL19fX7322msuiaOgoMAl56mNzMxMSaqYBudMPj4+CggIcPp5zqV169YaN26cbrnlFj355JP66aefZBiGy37/AOBMFEEAPNLUqVPVsGFDDRs2TDfccEOlIqikpEQRERG6/fbbq/xcbm6uAgIC9Oijj1ZsKyoq0sSJE9WyZUv5+/srISFBjz32mIqKiir9bPk0rKlTp6p9+/by9/fXnDlzJEn/+te/1KdPH0VGRiowMFBdu3bVV199VeX8p0+f1gMPPKBGjRopNDRUI0eO1OHDh2WxWDRp0qRK+x4+fFh33HGHYmJi5O/vr/bt2+uDDz64YG7Cw8P12muvaenSpXrvvfckSd98841mzZql559/XnFxcbLZbHr11VfVvn17BQQEKCYmRnfffbdOnDhR6Vjffvuthg0bpsaNG8vf318tWrTQs88+K6vVWmm/yy+/XCkpKVq7dq0uu+wyBQUF6YknnqgSW35+voKDg/Xggw9WeezQoUPy9vbWP/7xjwte49l+/vlnWSwWffnll3ruuecUHx+vgIAAXXnllZVGCJs1a6aJEydKkqKioqrNebni4mI9/fTT6tq1q8LDwxUcHKxLL71UCxcurNhn3759ioqKkiRNnjy5YupZ+TGruyeotLRUzz77rFq0aCF/f381a9ZMTzzxRJXnWrNmzTR8+HAtWbJEPXr0UEBAgJo3b66PP/64Vrk5W7t27dSoUSPt2bOn0vaa/I4vv/xyff/999q/f3/FdTZr1qzi8Zq+hgDAYQwA8EBt27Y1JkyYYBiGYSxevNiQZKxatari8TvuuMNo0KCBUVRUVOnnPvroI0OSsXr1asMwDMNqtRqDBg0ygoKCjIceesj473//a9x3332Gj4+Pcc0111T6WUlGu3btjKioKGPy5MnGG2+8Yaxfv94wDMOIj483/vjHPxqvv/668fLLLxs9evQwJBnfffddpWOMHj3akGTccsstxhtvvGGMHj3a6NSpkyHJmDhxYsV+R48eNeLj442EhATjmWeeMd566y1j5MiRhiTjlVdeqVGOhg0bZjRs2NDYs2ePkZCQYPTp08ew2WyGYRjGnXfeafj4+Bh33XWX8fbbbxt/+ctfjODgYKN79+5GcXFxxTFGjRpljB492njxxReNt956y7jxxhsNScajjz5a6Vz9+/c3YmNjjaioKOP+++83/vvf/xr/+9//Kh7r379/xb4333yzERMTY5SWllY6xj//+U/DYrEY+/fvP+91STLuvffeiu8XLlxoSDI6d+5sdO3a1XjllVeMSZMmGUFBQUaPHj0q9vvmm2+Ma6+91pBkvPXWW8Ynn3xibNy4sdoYjx07ZsTFxRmPPPKI8dZbbxn//Oc/jTZt2hi+vr4Vv/P8/HzjrbfeMiQZ1157rfHJJ59UOubEiRON3/43PX78eEOSccMNNxhvvPGGceuttxqSjFGjRlXaLzEx0WjTpo0RExNjPPHEE8brr79udOnSxbBYLEZqaup581NdjgzDME6ePGl4e3sbPXv2rLS9Jr/jH3/80bjkkkuMRo0aVVznN998YxhG7V5DAOAoFEEAPM6aNWsMSca8efMMwzAMm81mxMfHGw8++GDFPnPnzjUkGbNmzar0s1dffbXRvHnziu8/+eQTw8vLy/jll18q7ff2228bkoylS5dWbJNkeHl5GVu2bKkSU0FBQaXvi4uLjZSUFOOKK66o2LZ27VpDkvHQQw9V2ve2226rUgRNmDDBiIuLM7Kysirt+7vf/c4IDw+vcr7q7Nu3zwgODjYiIiIMX19fY/PmzYZhGMYvv/xiSDKmTp1aaf85c+ZU2V7dee6++24jKCjIKCwsrNjWv39/Q5Lx9ttvV9n/twVG+e9m9uzZlfbr2LFjpf3O5VxFULt27SoVva+99pohqeK6DePXwuTYsWPnjbG0tLRKAX3ixAkjJibGuOOOOyq2HTt2rMrv7rfnKrdhwwZDknHnnXdW2u/RRx81JBkLFiyo2JaYmGhIMhYvXlyxLTMz0/D39zf+9Kc/nSs1FSQZEyZMMI4dO2ZkZmYaa9asMYYMGWJIMl588cVK+9b0dzxs2DAjMTGxyr61eQ0BgKMwHQ6Ax5k6dapiYmI0YMAASWXT1MaMGaPPP/+8YgrPFVdcoUaNGumLL76o+LkTJ05o3rx5GjNmTMW26dOnq127dmrbtq2ysrIqvq644gpJqjT9SZL69++v5OTkKjEFBgZWOk9OTo4uvfRSrVu3rmJ7+dS5P/7xj5V+9v7776/0vWEY+vrrrzVixAgZhlEprsGDBysnJ6fScc8lMTFREydOVHZ2th555BGlpKRUXHN4eLgGDhxY6dhdu3ZVSEhIpWs++7ry8vKUlZWlSy+9VAUFBdq+fXul8/n7+1c7BfG3rrrqKjVu3LjSFMbU1FRt2rRJ48aNu+DPn8vtt98uPz+/iu8vvfRSSdLevXtrfSxvb++KY9lsNmVnZ6u0tFTdunWrUe6r88MPP0iSHnnkkUrb//SnP0lSlc5tycnJFdcglU3ha9OmTY2v5/3331dUVJSio6PVrVs3zZ8/X4899liV89fmd1yd2r6GAMARfMwOAABcyWq16vPPP9eAAQOUlpZWsb1nz5566aWXNH/+fA0aNEg+Pj66/vrrNW3aNBUVFcnf318zZsxQSUlJpSJo165d2rZtW8W9Hb9VfiN9uaSkpGr3++677/S3v/1NGzZsqHQfxNn3hOzfv19eXl5VjtGyZctK3x87dkwnT57UO++8c84Wzr+N61y6d+8uSerWrVvFtl27diknJ0fR0dEXPPaWLVv05JNPasGCBcrNza20X05OTqXvmzRpUqkIORcvLy/dfPPNeuutt1RQUKCgoCBNnTpVAQEBuvHGG2t0XdVp2rRppe8bNmwoSVXuc6qpjz76SC+99JK2b9+ukpKSiu3neg5cSPnv/7e/79jYWDVo0ED79++vtP231yOVXVNNr+eaa67Rfffdp+LiYq1evVp///vfVVBQIC+vyp+f1uZ3XJ3avoYAwBEoggB4lAULFig9PV2ff/65Pv/88yqPT506VYMGDZIk/e53v9N///tfzZ49W6NGjdKXX36ptm3bqlOnThX722w2dejQQS+//HK150tISKj0/dmfmpf75ZdfNHLkSF122WV68803FRcXJ19fX02ZMkXTpk2r9TXabDZJ0rhx4zR+/Phq9+nYsWOtj3v28aOjo8/ZUa/8zezJkyfVv39/hYWF6ZlnnlGLFi0UEBCgdevW6S9/+UtFnOWqy8253HrrrXrxxRf1v//9T2PHjtW0adM0fPhwhYeH231d3t7e1W43zuqQV1OffvqpbrvtNo0aNUp//vOfFR0dXdG04beNBWqrpguoXuz1xMfH66qrrpIkXX311WrUqJHuu+8+DRgwQNddd52k2v+Oq1Pb1xAAOAJFEACPMnXqVEVHR+uNN96o8tiMGTP0zTff6O2331ZgYKAuu+wyxcXF6YsvvlC/fv20YMEC/fWvf630My1atNDGjRt15ZVX1vjN6W99/fXXCggI0Ny5c+Xv71+xfcqUKZX2S0xMlM1mU1pamlq1alWx/bdrHEVFRSk0NFRWq7XiTawjtWjRQj/99JP69u173sLl559/1vHjxzVjxgxddtllFdvPHoGzV0pKijp37qypU6cqPj5eBw4c0H/+85+LPq6jfPXVV2revLlmzJhR6XlR3l2uXG2eM+W//127dqldu3YV2zMyMnTy5EklJiZefODncffdd+uVV17Rk08+qWuvvVYWi6VWv+NzXasjXkMAUFvcEwTAY5w+fVozZszQ8OHDdcMNN1T5uu+++5SXl6eZM2dKKpt2dcMNN2jWrFn65JNPVFpaWmkqnCSNHj1ahw8f1rvvvlvt+U6dOnXBuLy9vWWxWCq1FN63b5/+97//Vdpv8ODBkqQ333yz0vbfvvn39vbW9ddfr6+//lqpqalVznfs2LELxnQ+o0ePltVq1bPPPlvlsdLSUp08ebIiDqnyyENxcXGV+O11yy236Mcff9Srr76qyMhIDR061CHHdYTqrn3lypVavnx5pf2CgoIkqSJn53P11VdLUpUFR8tHUJy9gK2Pj4/+9Kc/adu2bfr2228l1e53HBwcXO30OEe8hgCgthgJAuAxZs6cqby8PI0cObLax3v16lWxcGp5sTNmzBj95z//0cSJE9WhQ4dKn8BLZW/Ev/zyS/3hD3/QwoUL1bdvX1mtVm3fvl1ffvml5s6dW+l+muoMGzZML7/8soYMGaKbbrpJmZmZeuONN9SyZUtt2rSpYr+uXbvq+uuv16uvvqrjx4+rV69eWrRokXbu3Cmp8iftzz//vBYuXKiePXvqrrvuUnJysrKzs7Vu3Tr99NNPys7OtiuHUllzh7vvvlv/+Mc/tGHDBg0aNEi+vr7atWuXpk+frtdee0033HCD+vTpo4YNG2r8+PF64IEHZLFY9Mknn9g1vaw6N910kx577DF98803uueee+Tr6+uQ4zrC8OHDNWPGDF177bUaNmyY0tLS9Pbbbys5OVn5+fkV+wUGBio5OVlffPGFWrdurYiICKWkpFQ0oThbp06dNH78eL3zzjsV09BWrVqljz76SKNGjapo9OFMt912m55++mm98MILGjVqVK1+x127dtUXX3yhRx55RN27d1dISIhGjBjhkNcQANSaaX3pAMDFRowYYQQEBBinTp065z633Xab4evrW9Fa2mazGQkJCYYk429/+1u1P1NcXGy88MILRvv27Q1/f3+jYcOGRteuXY3JkycbOTk5FfupmrVXyr3//vtGq1atDH9/f6Nt27bGlClTql0n5tSpU8a9995rREREGCEhIcaoUaOMHTt2GJKM559/vtK+GRkZxr333mskJCQYvr6+RmxsrHHllVca77zzTo3yZRi/to+ePn16lcfeeecdo2vXrkZgYKARGhpqdOjQwXjssceMI0eOVOyzdOlSo1evXkZgYKDRuHFj47HHHqtocb1w4cKK/fr372+0b9++2hh+2376bFdffbUhyVi2bFmNr+m3v4dzXWNaWpohyZgyZUrFtpq2yLbZbMbf//53IzEx0fD39zc6d+5sfPfdd8b48eOrtIletmyZ0bVrV8PPz69Su+zqfv8lJSXG5MmTjaSkJMPX19dISEgwHn/88UqtqA2jrEX2sGHDqlz7+XJ5tvM9VydNmlTp91fT33F+fr5x0003GQ0aNDAkVcpDTV9DAOAoFsNw0EdyAABTbNiwQZ07d9ann36qm2++2exwXOraa6/V5s2bq9wXBQDA+XBPEADUIadPn66y7dVXX5WXl1elG9M9QXp6ur7//nvdcsstZocCAKhjuCcIAOqQf/7zn1q7dq0GDBggHx8fzZ49W7Nnz9bvf/97j2klnJaWpqVLl+q9996Tr6+v7r77brNDAgDUMRRBAFCH9OnTR/PmzdOzzz6r/Px8NW3aVJMmTarSurs+W7RokW6//XY1bdpUH330kWJjY80OCQBQx3BPEAAAAACPwj1BAAAAADwKRRAAAAAAj1Kn7wmy2Ww6cuSIQkNDKy0SCAAAAMCzGIahvLw8NW7cWF5e5x/rqdNF0JEjRzymGxIAAACACzt48KDi4+PPu0+dLoJCQ0MllV1oWFiYqbGUlJToxx9/1KBBg+Tr62tqLHUNubMPebMPebMfubMPebMPebMPebMfubOPO+UtNzdXCQkJFTXC+dTpIqh8ClxYWJhbFEFBQUEKCwsz/QlQ15A7+5A3+5A3+5E7+5A3+5A3+5A3+5E7+7hj3mpymwyNEQAAAAB4FIogAAAAAB6FIggAAACAR6EIAgAAAOBRKIIAAAAAeBSKIAAAAAAehSIIAAAAgEehCAIAAADgUSiCAAAAAHgUiiAAAAAAHoUiCAAAAIBHoQgCAAAA4FEoggAAAAB4FIogAAAAeDSrzdDKtGytzbJoZVq2rDbD7JDgZD5mBwAAAACYZU5quibP2qr0nEJJ3vp41xrFhQdo4ohkDUmJMzs8OAkjQQAAAPBIc1LTdc+n684UQL86mlOoez5dpzmp6SZFBmejCAIAAIDHsdoMTZ61VdVNfCvfNnnWVqbG1VMUQQAAAPA4q9Kyq4wAnc2QlJ5TqFVp2a4LCi5DEQQAAACPk5l37gLInv1Qt1AEAQAAwONEhwY4dD/ULRRBAAAA8Dg9kiIUF37uAsciKS48QD2SIlwXFFyGIggAAAAex9vLookjks/5uCFp4ohkeXtZXBcUXIYiCAAAAB7pynYxCvLzrvaxZpFBGpQc6+KI4CoUQQAAAPBIK/dmq6DYqoggX310W1fd2sqqf4/pqCBfL+07XqDpaw+aHSKchCIIAAAAHmn2mcVQB6fEqk+LSHVtZGhoSqweGdRGkvT87O06carYzBDhJBRBAAAA8DhWm6G5WzIkSYPbV572Nr5PM7WJCdWJghK9+OMOM8KDk1EEAQAAwOOsP3BCWflFCg3wUZ8WjSo95uvtpWeuaS9J+mzVAW08eNKECOFMFEEAAADwOLNTj0qSrmoXIz+fqm+JezaP1LWdm8gwpKe+TZXVZrg6RDiR6UXQ4cOHNW7cOEVGRiowMFAdOnTQmjVrzA4LAAAA9ZRhGJpzpgj67VS4sz1+dVuF+vto06Ecfb76gKvCgwuYWgSdOHFCffv2la+vr2bPnq2tW7fqpZdeUsOGDc0MCwAAAPVY6uFcHT55WoG+3urfOuqc+0WHBuiRQa0lSf+cs0PZNEmoN3zMPPkLL7yghIQETZkypWJbUlKSiREBAACgvpuzpawr3OVtohR4jnWCyt3SK1Ffrjmkbem5emH2dr1wQ0dXhAgnM7UImjlzpgYPHqwbb7xRixYtUpMmTfTHP/5Rd911V7X7FxUVqaioqOL73NxcSVJJSYlKSkpcEvO5lJ/f7DjqInJnH/JmH/JmP3JnH/JmH/JmH/JWM7M3l02FG9guqkrOqsvdxGFt9Lv3VuuLNQd1fZc4dU5o4LJY3Z07PedqE4PFMAzT7vIKCAiQJD3yyCO68cYbtXr1aj344IN6++23NX78+Cr7T5o0SZMnT66yfdq0aQoKCnJ6vAAAAKjbjhZI/9joI2+Lob93syqghkMCU3d7adUxL8UHG/pTB6u8LM6NE7VXUFCgm266STk5OQoLCzvvvqYWQX5+furWrZuWLVtWse2BBx7Q6tWrtXz58ir7VzcSlJCQoKysrAteqLOVlJRo3rx5GjhwoHx9fU2Npa4hd/Yhb/Yhb/Yjd/Yhb/Yhb/Yhbxf2xs979er83bq8dSO9e0uXiu0Xyt3x/CINem2pcgtLNXF4W43r2dSVYbstd3rO5ebmqlGjRjUqgkydDhcXF6fk5ORK29q1a6evv/662v39/f3l7+9fZbuvr6/pSS/nTrHUNeTOPuTNPuTNfuTOPuTNPuTNPuTt3H7cmilJurpD42pzdK7cxTb01Z8Ht9FT327Ryz/t1ohL4tUopOr7Uk/lDs+52pzf1O5wffv21Y4dlVfh3blzpxITE02KCAAAAPXVgeMF2pqeK28vi65Kjqn1z9/UM1HtG4cpr7BUz8/e7oQI4SqmFkEPP/ywVqxYob///e/avXu3pk2bpnfeeUf33nuvmWEBAACgHirvCtczKUIRwX61/nlvL4ueHZUiSfpq7SGt2Zft0PjgOqYWQd27d9c333yjzz77TCkpKXr22Wf16quv6uabbzYzLAAAANRD5QukDkk59wKpF9KlaUP9rnuCJOnJ/6Wq1GpzSGxwLVPvCZKk4cOHa/jw4WaHAQAAgHosI7dQ6w6clCQNbm9/ESRJjw1pq9mpR7X9aJ4+WbFft/dlncu6xtSRIAAAAMAV5m4pGwXq0rSBYsICLupYEcF+emxIG0nSyz/uVGZu4UXHB9eiCAIAAEC954ipcGf7Xfem6hQfrryiUv2DJgl1DkUQAAAA6rXsU8VamVbWxGBI+ziHHNPby6JnrkmRxSJ9s/6wVu497pDjwjUoggAAAFCv/bQ1Q1aboeS4MDWNDHLYcTslNNDYHmWLpj71bapKaJJQZ1AEAQAAoF6bc+Z+oKEOmgp3tscGt1HDIF/tzMjXR8v2Ofz4cA6KIAAAANRbeYUlWrIrS5Lj7gc6W4MgP/3f0LaSpFfm7VQGTRLqBIogAAAA1FsLtmeq2GpT86hgtYwOcco5buyaoM5NG+hUsVV/+36bU84Bx6IIAgAAQL1V3hVuaEqsLBaLU87h5WXRs9ekyMsizdp4RMt2ZznlPHAciiAAAADUS6eLrfp5xzFJjusKdy4pTcI1rleiJOnpmVtUXEqTBHdGEQQAAIB6afGuYzpdYlWTBoFKaRLm9PP9aWAbRQb7aXdmvj5Ymub088F+FEEAAACol85eINVZU+HOFh7kq8evbidJ+vf8XTpy8rTTzwn7UAQBAACg3ikutemnbRmSnNMa+1yu69xE3RIbqqDYqudokuC2KIIAAABQ7yzbk6W8wlJFhfqrS9OGLjuvl5dFz5xpkvD95nQt3nnMZedGzVEEAQAAoN6Ze2aB1EHJMfLycv5UuLMlNw7T+D7NJEmTZm5RUanVpefHhVEEAQAAoF6x2gz9uKV8Kpxzu8Kdy8MDW6tRiL/2Zp3Se7/QJMHdUAQBAACgXlm9L1vHTxUrPNBXPZtHmBJDWICv/jqsrSTpPwt26dCJAlPiQPUoggAAAFCvlHeFG5gcI19v897ujrqkiXokRaiwxKZnv9tqWhyoiiIIAAAA9YbNZlTcDzSkveu6wlXHYrHo2WtS5O1l0dwtGVq4I9PUePAriiAAAADUG5sO5yg9p1DBft7q16qR2eGoTWyobj+rSUJhCU0S3AFFEAAAAOqN2anpkqQBbaMV4OttcjRlHhrYWjFh/tp/vEDvLN5rdjgQRRAAAADqCcMwNPfM/UBDXLhA6oWE+Pvor8OSJUlvLNytg9k0STAbRRAAAADqhR0Zedp3vEB+Pl4a0Cba7HAqGdExTr2bR6qo1KbJs7aYHY7HowgCAABAvTB7c9ko0GWtohTs72NyNJVZLBY9O6q9fLws+mlbpn7ammF2SB6NIggAAAD1QkVXODeaCne2ltGhmnBpkiRp8nc0STATRRAAAADqvLSsU9p+NE8+XhZd1c69psKd7YErWikuPEAHs0/rzZ/3mB2Ox6IIAgAAQJ1XvkBq7xaRahDkZ3I05xbs76Onhpc1SXh70R7tyzplckSeiSIIAAAAdd4cN58Kd7ahKbG6tFUjFZfaNGnWFhmGYXZIHociCAAAAHXakZOntfHgSVks0sDkGLPDuSCLxaJJI9vL19uin3cc0480SXA5iiAAAADUaeUNEbonRig6NMDkaGqmRVSIfn9Zc0nSM7O26nQxTRJciSIIAAAAddrsM/cDDa4DU+HOdu+AlmrSIFCHT57WGwt3mx2OR6EIAgAAQJ11LK9Iq/dlS5IGt3f/qXBnC/L7tUnCO4v3au+xfJMj8hwUQQAAAKizftqWIcOQOsaHK75hkNnh1Nrg9jG6vE2Uiq02TZxJkwRXoQgCAABAnVUxFa593ZoKV85isWjSiPby8/bSL7uyKlp9w7koggAAAFAn5Zwu0bLdWZLK2k7XVc0aBesP/c80Sfhuq04VlZocUf1HEQQAAIA6af62DJXaDLWOCVHzqBCzw7kofxzQUvENA5WeU6j/LKBJgrNRBAEAAKBOKp86NqSOToU7W4CvtyaNaC9Jeu+XvdqdmWdyRPUbRRAAAADqnFNFpVq085gkaUhKnMnROMZVyTG6sm20Sm2Gnv6WJgnORBEEAACAOmfRzmMqKrWpaUSQ2sWFmh2Ow0wa2V7+Pl5atue4vtuUbnY49RZFEAAAAOqc8qlwQ1NiZbFYTI7GcRIigvTHy1tKkv72/Vbl0yTBKSiCAAAAUKcUlVq1YHumJGlwHe4Kdy5392+uxMggZeQW6bWfdpodTr1EEQQAAIA6ZenuLOUXlSomzF+XxDcwOxyHC/D11qSRZU0SPli6TzuO0iTB0SiCAAAAUKfM3vxrVzgvr/ozFe5sA9pEa1ByjKw2Q09/m0qTBAejCAIAAECdUWq1ad62DEn1cyrc2Z4ekawAXy+tTMvWtxuOmB1OvUIRBAAAgDpjVVq2ThaUKCLYTz2aRZgdjlPFNwzS/Ve0kiQ998M25RaWmBxR/UERBAAAgDpj9pmucAPbxcjHu/6/lb3z0iQlNQrWsbwivTpvl9nh1Bv1/5kDAACAesFmMzR3y5n7gTrU76lw5fx9vDX5TJOEj5bv07b0XJMjqh8oggAAAFAnrD94Qpl5RQr191GfFpFmh+Myl7WO0tUdYmW1GXrqfzRJcASKIAAAANQJ5QukXtEuWv4+3iZH41pPDktWoK+31uw/oRnrDpsdTp1HEQQAAAC3ZxiG5pyZCje0nneFq07jBoF64MqyJgn/mL1NOadpknAxKIIAAADg9rYcydXB7NMK8PXSZa2jzA7HFBP6JalFVLCy8ov18o87zA6nTqMIAgAAgNsrb4hweetoBfn5mByNOfx8vPTMNSmSpE9W7Ffq4RyTI6q7KIIAAADg9spbYw/xwKlwZ+vbspGGd4yTzZCe+jZVNhtNEuxBEQQAAAC3tjszT7sz8+XrbdGAttFmh2O6J4clK9jPW+sPnNRXaw+ZHU6dRBEEAAAAtzZ3S4akslGQ8EBfk6MxX2x4gB66qrUk6fk523WyoNjkiOoeiiAAAAC4tdmp6ZKkIe09eyrc2W7r20ytY0KUfapYL86lSUJtUQQBAADAbR3MLlDq4Vx5WaSByTFmh+M2fL1/bZIwbdUBbTp00tyA6hiKIAAAALit8q5wPZIiFBnib3I07qVX80iNuqSxDEN66n80SagNiiAAAAC4rTnlXeGYCletJ65up1B/H208lKPPVx80O5w6gyIIAAAAbikzt1BrD5yQJA328NbY5xIdFqCHB5Y1Sfjn3O3KPkWThJqgCAIAAIBbmrs1Q4YhXZLQQHHhgWaH47Zu7Z2otrGhOllQohfnbjc7nDqBIggAAABuae6ZqXBDGQU6Lx9vLz07qqxJwuerD2r9mdEznBtFEAAAANzOiVPFWr73uCRpCEXQBXVvFqHru8SXNUn4NlVWmiScF0UQAAAA3M5P2zJktRlqFxemxMhgs8OpE/5vaFuFBvgo9XCupq06YHY4bo0iCAAAAG6HrnC1FxXqr0cHtZEkvThnu7Lyi0yOyH1RBAEAAMCt5BeV6pddWZKYCldb43olqn3jMOUWluqF2TRJOBeKIAAAALiVhdszVWy1qXmjYLWOCTE7nDrF28uiZ64pa5Iwfe0hrd2fbXJE7okiCAAAAG6lfCrc4JRYWSwWk6Ope7omNtTobvGSpCf/t0WlVpvJEbkfiiAAAAC4jcISqxbuyJREa+yL8ZchbRUe6Ktt6bn6dMV+s8NxOxRBAAAAcBuLdx5TQbFVjcMD1KFJuNnh1FmRIf768+CyJgkv/bhTx/JoknA2iiAAAAC4jTlbmArnKGN7NFXH+HDlFZXqHz9sMzsct0IRBAAAALdQYrXpp60ZkqShKXEmR1P3eXtZ9Ow1KbJYpBnrD2vlmcVnQREEAAAAN7F8z3HlFpaqUYifuiY2NDuceqFTQgP9rntTSdLT325RCU0SJFEEAQAAwE2UT4Ub1D5W3l5MhXOUxwa3UcMgX+3IyNNHy/aZHY5boAgCAACA6aw2Qz+eKYKGtKcrnCM1DPbTX4a0lSS9+tMuZeQWmhyR+SiCAAAAYLq1+08oK79YYQE+6tU80uxw6p3R3RLUKaGB8otK9XeaJFAEAQAAwHyzU9MlSVclx8jPh7eojublZdHfzjRJ+HbDES3bk2V2SKbiGQYAAABTGYahualMhXO2DvHhGtczURJNEiiCAAAAYKrNh3N0JKdQQX7euqx1lNnh1GuPDmqjiGA/7c7M15SlaWaHYxqKIAAAAJhq9plRoAFtohXg621yNPVbeJCv/m/or00S0nNOmxyROSiCAAAAYBrDMDSnfCpcClPhXOGGLvHqmthQBcVW/e17z2ySQBEEAAAA0+zMyFda1in5eXtpQNtos8PxCF5eFj1zTXt5WaTvN6VryS7Pa5JAEQQAAADTlI8CXdqqkUL8fUyOxnO0bxyuW3s3kyQ9PTNVxaWe1SSBIggAAACmmbOFqXBmeXhgazUK8dfeY6f03pK9ZofjUqYWQZMmTZLFYqn01bZtWzNDAgAAgIvsP35K29Jz5e1l0VXtYswOx+OEB/rqiavL3nv/Z/5uHT7pOU0STB8Jat++vdLT0yu+lixZYnZIAAAAcIHyqXC9m0eqYbCfydF4pms7N1GPZhE6XWLV377banY4LmN6EeTj46PY2NiKr0aNGpkdEgAAAFygvDX2YKbCmcZiseiZUe3l7WXR7NSjWrTzmNkhuYTpd5/t2rVLjRs3VkBAgHr37q1//OMfatq0abX7FhUVqaioqOL73NxcSVJJSYlKSkpcEu+5lJ/f7DjqInJnH/JmH/JmP3JnH/JmH/Jmn7qUt/ScQm04eFIWi3RF60jTY65LuXO0FpGBurVXU01Ztl9P/y9V39/fR/4+NRsrcae81SYGi2EYhhNjOa/Zs2crPz9fbdq0UXp6uiZPnqzDhw8rNTVVoaGhVfafNGmSJk+eXGX7tGnTFBQU5IqQAQAA4ACL0y36ep+3kkINPZRiNTscj1dYKj23wVu5JRYNS7BqULxpJYLdCgoKdNNNNyknJ0dhYWHn3dfUIui3Tp48qcTERL388suaMGFClcerGwlKSEhQVlbWBS/U2UpKSjRv3jwNHDhQvr6+psZS15A7+5A3+5A3+5E7+5A3+5A3+9SlvI37YLVWpp3Q40Na646+zcwOp07lzllmbUrXI9M3K8DXS7Pv76v4hoEX/Bl3yltubq4aNWpUoyLI9OlwZ2vQoIFat26t3bt3V/u4v7+//P39q2z39fU1Penl3CmWuobc2Ye82Ye82Y/c2Ye82Ye82cfd83Y8v0ir952QJF3dsYlbxeruuXOma7sk6Mu1h7Vib7b+Pmen3r21W41/1h3yVpvzm94Y4Wz5+fnas2eP4uLizA4FAAAATjJva4ZshpTSJEwJEdzS4C4sFouevSZFPl4WzduaoQXbM8wOyWlMLYIeffRRLVq0SPv27dOyZct07bXXytvbW2PHjjUzLAAAADhRxQKp7ekK525axYRqQr8kSdKkmVtVWFI/79cytQg6dOiQxo4dqzZt2mj06NGKjIzUihUrFBUVZWZYAAAAcJLcwhIt3Z0lSRqSwuwfd3T/la0UGxagA9kFenvRHrPDcQpT7wn6/PPPzTw9AAAAXGzBtkyVWA21jA5Ry+gQs8NBNUL8ffTk8Ha6b9p6vfnzHl3XOV5NI+vXtEW3uicIAAAA9ducMwukDmWBVLc2rEOc+rVspOJSmybN2iI3aijtEBRBAAAAcImC4lL9vDNTkjSY+4HcmsVi0aSR7eXrbdGC7Zn6aVum2SE5FEUQAAAAXGLxzmMqLLEpISJQ7Rubu8YjLqxldIjuvLS5JGnSzC06XVx/miRQBAEAAMAlZqf+2hXOYrGYHA1q4v4rWqpxeIAOnzytN3+ufi3PuogiCAAAAE5XVGrVgjNTqoZwP1CdEeTno6dHJEuS/rtor9KyTpkckWNQBAEAAMDplu05rryiUkWH+qtzQkOzw0EtDG4fq8taR6nYatPEmfWjSQJFEAAAAJxuzuayqXCD28fKy4upcHWJxWLR5JHt5eftpcU7j2numcVu6zKKIAAAADhVqdWmedsyJNEau65KahSsu/uXNUl4ZtZWFRSXmhzRxaEIAgAAgFOt2pet7FPFahDkqx5JEWaHAzv98fKWatIgUEdyCvX6grrdJIEiCAAAAE4190xXuIHtYuTjzdvPuirQz1uTRraXJL37y17tOJqnlWnZWptl0cq0bFltdedeIR+zAwAAAED9ZbMZmrvlzFS4DkyFq+uuahetK9pGa8H2TI34zxIVW22SvPXxrjWKCw/QxBHJGpISZ3aYF0QpDgAAAKfZcOikjuYWKsTfR31bNjI7HFwki8WiAW2iJOlMAfSrozmFuufTdZqTmm5GaLVCEQQAAACnKZ8Kd0XbaPn7eJscDS6W1WbozZ/3VPtY+WS4ybO2uv3UOIogAAAAOIVhGJp9pghigdT6YVVattJzCs/5uCEpPadQq9KyXReUHSiCAAAA4BTb0vN0ILtA/j5e6t86yuxw4ACZeecugOzZzywUQQAAAHCKOWcW1ezfOkrB/vTjqg+iQwMcup9ZKIIAAADgFOU3yDMVrv7okRShuPAAWc7xuEVSXHiA268HRREEAAAAh9tzLF87M/Ll42XRle1izA4HDuLtZdHEEcmSVKUQKv9+4ohkeXudq0xyDxRBAAAAcLg5Zxoi9GnZSOGBviZHA0cakhKnt8Z1UWx45SlvseEBemtclzqxThCTMwEAAOBwc8/cDzSUqXD10pCUOA1MjtXy3Zn68ZeVGnRpT/VuGe32I0DlKIIAAADgUIdOFGjToRxZLNLAZKbC1VfeXhb1TIrQ8W2GeiZF1JkCSGI6HAAAABxs7pYMSVL3ZhFqFOJvcjRAVRRBAAAAcKi5qUyFg3ujCAIAAIDDZOYVavX+bEnS4PYUQXBPFEEAAABwmHlbM2QYUqeEBmrcINDscIBqUQQBAADAYcpbYw9hFAhujCIIAAAADnGyoFjL9xyXJA3hfiC4MYogAAAAOMT8bZkqtRlqGxuqpEbBZocDnBNFEAAAABxi9pmpcDREgLujCAIAAMBFO1VUqsW7jkmShnagCIJ7owgCAADARVu4I1PFpTY1iwxSm5hQs8MBzosiCAAAABetvCvc4JRYWSwWk6MBzo8iCAAAABelsMSqhdszJUlDU+JMjga4MIogAAAAXJQlu7J0qtiquPAAdWwSbnY4wAVRBAEAAOCizNnya1c4Ly+mwsH9UQQBAADAbiVWm+ZtzZDEAqmoO3xq+wNFRUVauXKl9u/fr4KCAkVFRalz585KSkpyRnwAAABwYyv3ZivndIkig/3UvVmE2eEANVLjImjp0qV67bXXNGvWLJWUlCg8PFyBgYHKzs5WUVGRmjdvrt///vf6wx/+oNBQ2iICAAB4gjlb0iVJg9rHyJupcKgjajQdbuTIkRozZoyaNWumH3/8UXl5eTp+/LgOHTqkgoIC7dq1S08++aTmz5+v1q1ba968ec6OGwAAACaz2QzN3VI2FW5we6bCoe6o0UjQsGHD9PXXX8vX17fax5s3b67mzZtr/Pjx2rp1q9LT0x0aJAAAANzPugMndCyvSKEBPurTopHZ4QA1VqMi6O67767xAZOTk5WcnGx3QAAAAKgbZp9ZIPWqdjHy86HfFuqOWjdGOFtqaqoWLVokq9Wqvn37qmvXro6KCwAAAG7MMAzNOVME0RUOdY3dJfsbb7yhK6+8UosWLdLChQt1xRVX6LnnnnNkbAAAAHBTqYdzdfjkaQX6euuyVlFmhwPUSo1Hgg4ePKiEhISK719//XVt2bJFjRqVzf9cvny5Ro4cqb/+9a+OjxIAAABupbwr3OVtohTo521yNEDt1Hgk6KqrrtJrr70mwzAkSZGRkZozZ46KioqUl5enn376SVFRfAoAAADgCZgKh7qsxkXQ6tWrtWPHDvXs2VMbNmzQO++8o1deeUWBgYFq0KCBvvjiC3300UfOjBUAAABuYFdGnvYcOyU/by9d0Tba7HCAWqvxdLiwsDC9+eabWrZsmW677TZdccUV+uWXX2S1WmW1WtWgQQMnhgkAAAB3UT4K1K9VI4UGVL+ECuDOat0YoU+fPlqzZo0aNmyozp07a/HixRRAAAAAHqS8NfYQFkhFHVXjkaDS0lK988472rZtmzp16qQnnnhCY8aM0R/+8Ad9+OGHev311xUTE+PMWAEAAGCyA8cLtDU9V95eFl2VzHs/1E01HgmaMGGCXn/9dQUHB2vKlCl6+OGH1bp1ay1YsEBDhgxR79699dZbbzkzVgAAAJhs7payUaCeSRGKCPYzORrAPjUugr799lt9/fXXev755zVv3jx9//33FY9NmDBBK1as0C+//OKUIAEAAOAeZqeWtcamKxzqshoXQTExMfrxxx9VXFysBQsWKDIystLj0dHRmjZtmsMDBAAAgHvIyC3UugMnJUmDuR8IdViN7wl6/fXXdfPNN+uRRx5RXFycvvzyS2fGBQAAADdTPhWuS9MGigkLMDkawH41LoIGDhyojIwMZWVlsSgqAACABypvjT00Jc7kSICLU6sW2RaLhQIIAADAA2WfKtbKtGxJTIVD3VejImjIkCFasWLFBffLy8vTCy+8oDfeeOOiAwMAAID7+Glrhqw2Q8lxYWoaGWR2OMBFqdF0uBtvvFHXX3+9wsPDNWLECHXr1k2NGzdWQECATpw4oa1bt2rJkiX64YcfNGzYML344ovOjhsAAAAuNGdL+VQ4RoFQ99WoCJowYYLGjRun6dOn64svvtA777yjnJwcSWVT5JKTkzV48GCtXr1a7dq1c2rAAAAAcK28whIt2ZUlidbYqB9q3BjB399f48aN07hx4yRJOTk5On36tCIjI+Xr6+u0AAEAAGCuBdszVWy1qUVUsFrFhJodDnDRalwE/VZ4eLjCw8MdGQsAAADcUHlrbEaBUF/UqjscAAAAPMvpYqsWbj8mSRrSntbYqB8oggAAAHBOi3cd0+kSq5o0CFRKkzCzwwEcgiIIAAAA51S+QOqQlFhZLBaTowEcgyIIAAAA1SoutemnbRmSaI2N+sWuIujkyZN677339Pjjjys7u2zl4HXr1unw4cMODQ4AAADmWbYnS3mFpYoK9VeXpg3NDgdwmFp3h9u0aZOuuuoqhYeHa9++fbrrrrsUERGhGTNm6MCBA/r444+dEScAAABcrLwr3KDkGHl5MRUO9UetR4IeeeQR3Xbbbdq1a5cCAgIqtl999dVavHixQ4MDAACAOaw2Qz9uKZ8KR1c41C+1LoJWr16tu+++u8r2Jk2a6OjRow4JCgAAAOZavS9bx08VKzzQVz2bR5gdDuBQtS6C/P39lZubW2X7zp07FRUV5ZCgAAAAYK7yrnADk2Pk600vLdQvtX5Gjxw5Us8884xKSkokSRaLRQcOHNBf/vIXXX/99Q4PEAAAAK5lsxkV9wMNaU9XONQ/tS6CXnrpJeXn5ys6OlqnT59W//791bJlS4WGhuq5555zRowAAABwoU2Hc5SeU6hgP2/1a9XI7HAAh6t1d7jw8HDNmzdPS5cu1caNG5Wfn68uXbroqquuckZ8AAAAcLHyqXAD2kYrwNfb5GgAx6t1EfTxxx9rzJgx6tu3r/r27Vuxvbi4WJ9//rluvfVWhwYIAAAA1zEMQ3NS0yVJQ1ggFfVUrafD3X777crJyamyPS8vT7fffrtDggIAAIA5dmTkad/xAvn5eGlAm2izwwGcotZFkGEYsliqLpZ16NAhhYeHOyQoAAAAmGP25rKpcJe1ilKwf60nDQF1Qo2f2Z07d5bFYpHFYtGVV14pH59ff9RqtSotLU1DhgxxSpAAAABwjfKucEOZCod6rMZF0KhRoyRJGzZs0ODBgxUSElLxmJ+fn5o1a0aLbAAAgDosLeuUth/Nk4+XRVe2Yyoc6q8aF0ETJ06UJDVr1kxjxoxRQECA04ICAACA65V3hevdIlINgvxMjgZwnlpP9Bw/frwz4gAAAIDJ5pQvkMpUONRztS6CrFarXnnlFX355Zc6cOCAiouLKz2enZ3tsOAAAADgGkdOntbGgydlsUgDk2PMDgdwqlp3h5s8ebJefvlljRkzRjk5OXrkkUd03XXXycvLS5MmTXJCiAAAAHC28oYI3RMjFB3KbQ+o32pdBE2dOlXvvvuu/vSnP8nHx0djx47Ve++9p6efflorVqxwRowAAABwstln7gcazFQ4eIBaF0FHjx5Vhw4dJEkhISEVC6cOHz5c33//vWOjAwAAgNMdyyvS6n1ltzQMbs9UONR/tS6C4uPjlZ6eLklq0aKFfvzxR0nS6tWr5e/v79joAAAA4HQ/bcuQYUgd48MV3zDI7HAAp6t1EXTttddq/vz5kqT7779fTz31lFq1aqVbb71Vd9xxh92BPP/887JYLHrooYfsPgYAAABqr2IqXHumwsEz1Lo73PPPP1/x9zFjxigxMVHLli1Tq1atNGLECLuCWL16tf773/+qY8eOdv08AAAA7JNzukTLdmdJkoZyPxA8RK1Hgn6rV69eeuSRRzRixAitWbOm1j+fn5+vm2++We+++64aNmx4seEAAACgFuZvy1CpzVDrmBA1jwoxOxzAJWo9EpSfny9vb28FBgZWbNuwYYOeeuop/fDDD7JarbU63r333qthw4bpqquu0t/+9rfz7ltUVKSioqKK73NzcyVJJSUlKikpqdV5Ha38/GbHUReRO/uQN/uQN/uRO/uQN/uQN/vYk7fZm8vu9R7ULtqj881zzj7ulLfaxGAxDMOoyY4HDx7U6NGjtWrVKnl7e+u+++7T3/72N/3hD3/QF198oWuvvVYPP/ywevbsWeOTf/7553ruuee0evVqBQQE6PLLL9cll1yiV199tdr9J02apMmTJ1fZPm3aNAUFcRMfAABAbRRZpb+u9laJYdFjHUvVJNjsiAD7FRQU6KabblJOTo7CwsLOu2+NR4L+/Oc/q7CwUK+99ppmzJih1157Tb/88ot69uypPXv2KD4+vlZBHjx4UA8++KDmzZungICaLcj1+OOP65FHHqn4Pjc3VwkJCRo0aNAFL9TZSkpKNG/ePA0cOFC+vr6mxlLXkDv7kDf7kDf7kTv7kDf7kDf71DZvs1OPqmTVJiU0DNSdN/STxWJxQZTuieecfdwpb+WzxGqixkXQ4sWLNWPGDPXq1UujR49WbGysbr75Zru7ua1du1aZmZnq0qVLxTar1arFixfr9ddfV1FRkby9vSv9jL+/f7VtuH19fU1Pejl3iqWuIXf2IW/2IW/2I3f2IW/2IW/2qWneftpe1hDh6g5x8vPzc3ZYdQLPOfu4Q95qc/4aF0EZGRlKSkqSJEVHRysoKEhDhw6tfXRnXHnlldq8eXOlbbfffrvatm2rv/zlL1UKIAAAADhOUalVC7ZnSpIG0xUOHqZWjRG8vLwq/f1iPjEIDQ1VSkpKpW3BwcGKjIyssh0AAACOtXR3lvKLShUbFqBL4huYHQ7gUjUuggzDUOvWrSvmiubn56tz586VCiNJys7OdmyEAAAAcLg5FQukxsjLy3PvBYJnqnERNGXKFGfGIUn6+eefnX4OAAAAT1dqtWne1gxJTIWDZ6pxETR+/HhnxgEAAAAXWZWWrRMFJYoI9lOPZhFmhwO4nNeFdwEAAEB9MvvMVLiB7WLk483bQXgenvUAAAAexGYzNHdLWRE0pANT4eCZKIIAAAA8yPqDJ5WZV6RQfx/1aRFpdjiAKSiCAAAAPMic1HRJ0hXtouXvw7qM8EwUQQAAAB7CMAzNOTMVbihd4eDBarVYqiRZrVZ9+OGHmj9/vjIzM2Wz2So9vmDBAocFBwAAAMfZciRXB7NPK8DXS5e1jjI7HMA0tS6CHnzwQX344YcaNmyYUlJSKhZPBQAAgHsrb4hweetoBfnV+m0gUG/U+tn/+eef68svv9TVV1/tjHgAAADgJOWtsYcwFQ4ertb3BPn5+ally5bOiAUAAABOsjszT7sz8+XrbdEV7aLNDgcwVa2LoD/96U967bXXZBiGM+IBAACAE8zdkiFJ6tuykcICfE2OBjBXrafDLVmyRAsXLtTs2bPVvn17+fpWfhHNmDHDYcEBAADAMWafaY09pD1T4YBaF0ENGjTQtdde64xYAAAA4AQHswuUejhXXhZpYHKM2eEApqt1ETRlyhRnxAEAAAAnKe8K1yMpQpEh/iZHA5jP7t6Ix44d044dOyRJbdq0UVQUveYBAADc0ZzU8gVS40yOBHAPtW6McOrUKd1xxx2Ki4vTZZddpssuu0yNGzfWhAkTVFBQ4IwYAQAAYKfM3EKtPXBCkjSoPVPhAMmOIuiRRx7RokWLNGvWLJ08eVInT57Ut99+q0WLFulPf/qTM2IEAACAneZuzZBhSJckNFBceKDZ4QBuodbT4b7++mt99dVXuvzyyyu2XX311QoMDNTo0aP11ltvOTI+AAAAXIS5FVPh6AoHlKv1SFBBQYFiYqoOpUZHRzMdDgAAwI2cOFWs5XuPS5KGUAQBFWpdBPXu3VsTJ05UYWFhxbbTp09r8uTJ6t27t0ODAwAAgP1+2pYhq81Qu7gwJUYGmx0O4DZqPR3utdde0+DBgxUfH69OnTpJkjZu3KiAgADNnTvX4QECAADAPuWtsVkgFais1kVQSkqKdu3apalTp2r79u2SpLFjx+rmm29WYCA32wEAALiD/KJSLd6VJYmpcMBv2bVOUFBQkO666y5HxwIAAAAHWbg9U8WlNjVvFKzWMSFmhwO4lRoVQTNnztTQoUPl6+urmTNnnnffkSNHOiQwAAAA2K98gdTBKbGyWCwmRwO4lxoVQaNGjdLRo0cVHR2tUaNGnXM/i8Uiq9XqqNgAAABgh8ISqxbuyJREa2ygOjUqgmw2W7V/BwAAgPtZuvu4CoqtatIgUB2ahJsdDuB2at0i++OPP1ZRUVGV7cXFxfr4448dEhQAAADsN3drhiRpcHumwgHVqXURdPvttysnJ6fK9ry8PN1+++0OCQoAAAD2sdqk+duPSaIrHHAutS6CDMOo9hOFQ4cOKTyc4VYAAAAzWG2GVqZl64eDXsotLFVksK+6JjY0OyzALdW4RXbnzp1lsVhksVh05ZVXysfn1x+1Wq1KS0vTkCFDnBIkAAAAzm1Oaromz9qq9JxClX/GfbrEpnlbj2pISpy5wQFuqMZFUHlXuA0bNmjw4MEKCfm137yfn5+aNWum66+/3uEBAgAA4NzmpKbrnk/XyfjN9oJiq+75dJ3eGteFQgj4jRoXQRMnTpQkNWvWTGPGjFFAQIDTggIAAMCFWW2GJs/aWqUAOtvkWVs1MDlW3l40SADK1fqeoPHjx1MAAQAAuIFVadlnpsBVz5CUnlOoVWnZrgsKqANqPBJUzmq16pVXXtGXX36pAwcOqLi4uNLj2dm8yAAAAFwhM+/cBZA9+wGeotYjQZMnT9bLL7+sMWPGKCcnR4888oiuu+46eXl5adKkSU4IEQAAANWJDq3Z7Jya7gd4iloXQVOnTtW7776rP/3pT/Lx8dHYsWP13nvv6emnn9aKFSucESMAAACq0SMpQnHhATrX3T4WSXHhAeqRFOHKsAC3V+si6OjRo+rQoYMkKSQkpGLh1OHDh+v77793bHQAAAA4J28viyaOSK62MUJ5YTRxRDJNEYDfqHURFB8fr/T0dElSixYt9OOPP0qSVq9eLX9/f8dGBwAAgPMa3D5WiZFBVbbHhgfQHhs4h1o3Rrj22ms1f/589ezZU/fff7/GjRun999/XwcOHNDDDz/sjBgBAABwDmv2n9D+4wXy9bbo1dEdtXLNOg26tKd6t4xmBAg4h1oXQc8//3zF38eMGaOmTZtq+fLlatWqlUaMGOHQ4AAAAHB+7/+SJkm6oWu8BiXHqHSfoZ5JERRAwHnUugj6rd69e6t3796OiAUAAAC1cOB4geZuPSpJuqNvksnRAHVHjYqgmTNn1viAI0eOtDsYAAAA1NyUZWkyDOmy1lFqFROqkpISs0MC6oQaFUGjRo2q0cEsFousVuvFxAMAAIAayC0s0ZerD0qS7uzHKBBQGzUqgmw2m7PjAAAAQC18seqgThVb1TomRJe2amR2OECdUqMW2RERETp+/Lgk6Y477lBeXp5TgwIAAMC5lVpt+nDZPknShH5JslhoggDURo2KoOLi4opFUT/66CMVFhY6NSgAAACc25wtR3X45GlFBvvpmkuamB0OUOfUaDpc7969NWrUKHXt2lWGYeiBBx5QYGBgtft+8MEHDg0QAAAAlb13pi32uF6JCvD1NjkaoO6pURH06aef6pVXXtGePXtksViUk5PDaBAAAIAJ1u4/oQ0HT8rP20vjeiWaHQ5QJ9WoCIqJialYJDUpKUmffPKJIiMjnRoYAAAAqnp/yV5J0qjOjRUV6m9yNEDdVOvFUtPS0pwRBwAAAC7gYHaB5qSeWRyVttiA3WpdBEnS/PnzNX/+fGVmZlZpn809QQAAAM7x4bJ9shnSpa0aqW1smNnhAHVWrYugyZMn65lnnlG3bt0UFxdHS0YAAAAXyCss0RdnFkdlFAi4OLUugt5++219+OGHuuWWW5wRDwAAAKrxxeqDyi8qVcvoEPVvFWV2OECdVqN1gs5WXFysPn36OCMWAAAAVOPsxVHv6JskLy9m4gAXo9ZF0J133qlp06Y5IxYAAABU48etGTp04rQaBvnqui4sjgpcrFpPhyssLNQ777yjn376SR07dpSvr2+lx19++WWHBQcAAADp/SUsjgo4Uq2LoE2bNumSSy6RJKWmplZ6jCYJAAAAjrX+wAmt3X9Cft5euqU3i6MCjlDrImjhwoXOiAMAAADVKB8FGtGpsaJDA0yOBqgfan1PEAAAAFzj8MnTmn1mcdQJtMUGHKbGI0HXXXddjfabMWOG3cEAAADgVx8t2yerzVCfFpFKbsziqICj1LgICg8Pd2YcAAAAOEt+Uak+W3lAknTnpYwCAY5U4yJoypQpzowDAAAAZ5m+5qDyikrVPCpYl7eONjscoF7hniAAAAA3Y7UZ+mBpWUMEFkcFHI8iCAAAwM3M25qhg9mn1SDIV9d3iTc7HKDeoQgCAABwM+8v2StJurlnUwX6sTgq4GgUQQAAAG5k48GTWr3vhHy9Lbq1dzOzwwHqJYogAAAAN1KxOGrHxooJY3FUwBkoggAAANzEkZOn9cPmdEnSHSyOCjgNRRAAAICb+Gj5PpXaDPVqHqGUJqzRCDgLRRAAAIAbOHXW4qgT+jU3ORqgfqMIAgAAcANfrT2k3MJSNYsM0pVtWRwVcCaKIAAAAJNZbYamlC+O2o/FUQFnowgCAAAw2fxtGdp3vEDhgb66oSuLowLORhEEAABgsvK22GN7NFWQn4/J0QD1H0UQAACAiVIP52hlWrZ8vCwa3yfR7HAAj0ARBAAAYKLyUaBhHeMUFx5ocjSAZ6AIAgAAMMnRnELN2nhEkjSBxVEBl6EIAgAAMMnHZxZH7dEsQh3jG5gdDuAxKIIAAABMUFBcqmmryhZHvYNRIMClKIIAAABM8PW6wzpZUKKmEUEamBxjdjiAR6EIAgAAcDGbzdCUMw0Rbu/bTN4sjgq4FEUQAACAiy3ckam9WacUGuCjG7slmB0O4HEoggAAAFzs7MVRQ/xZHBVwNVOLoLfeeksdO3ZUWFiYwsLC1Lt3b82ePdvMkAAAAJxqy5EcLdtzXN5eFo3v08zscACPZGoRFB8fr+eff15r167VmjVrdMUVV+iaa67Rli1bzAwLAADAaT5Ysk+SNDQlVk0asDgqYAZTx19HjBhR6fvnnntOb731llasWKH27dubFBUAAIBzZOYWaubGw5KkOy9tbnI0gOdym0moVqtV06dP16lTp9S7d+9q9ykqKlJRUVHF97m5uZKkkpISlZSUuCTOcyk/v9lx1EXkzj7kzT7kzX7kzj7kzT71NW8fLk1TidVQl6YN1D422OHXV1/z5grkzj7ulLfaxGAxDMNwYiwXtHnzZvXu3VuFhYUKCQnRtGnTdPXVV1e776RJkzR58uQq26dNm6agoCBnhwoAAGC3Yqs0aZ23TpVadHtrqy6JNPUtGFDvFBQU6KabblJOTo7CwsLOu6/pRVBxcbEOHDignJwcffXVV3rvvfe0aNEiJScnV9m3upGghIQEZWVlXfBCna2kpETz5s3TwIED5evra2osdQ25sw95sw95sx+5sw95s099zNvnqw/pqZlbFd8gQD89fKlT1gaqj3lzFXJnH3fKW25urho1alSjIsj06XB+fn5q2bKlJKlr165avXq1XnvtNf33v/+tsq+/v7/8/f2rbPf19TU96eXcKZa6htzZh7zZh7zZj9zZh7zZp77kzWYz9OHy/ZKk2/s1V4C/n1PPV1/yZgZyZx93yFttzu926wTZbLZKoz0AAAB13aJdx7Tn2CmF+PtodLd4s8MBPJ6pI0GPP/64hg4dqqZNmyovL0/Tpk3Tzz//rLlz55oZFgAAgEO9/0vZ4qi/656g0ABGGQCzmVoEZWZm6tZbb1V6errCw8PVsWNHzZ07VwMHDjQzLAAAAIfZfjRXS3ZnycsiFkcF3ISpRdD7779v5ukBAACcrnwUaGhKnBIi6GYLuAO3uycIAACgvjiWV6RvNxyRJN3RL8nkaACUowgCAABwkk9W7Fex1abOTRuoa2JDs8MBcAZFEAAAgBMUllg1dUVZW+wJjAIBboUiCAAAwAn+t/6wjp8qVpMGgRrSPtbscACchSIIAADAwQzD0PtLyhoi3NanmXy8ecsFuBNekQAAAA62eFeWdmXmK9jPW2N6JJgdDoDfoAgCAABwsPJRoNHdExTG4qiA26EIAgAAcKCdGXlavPOYvCzS7X1oiAC4I4ogAAAAB/rgzCjQoORYNY1kcVTAHVEEAQAAOEhWfpFmrD8sSbrzUkaBAHdFEQQAAOAgU1ccUHGpTZ3iw1kcFXBjFEEAAAAOUFhi1Scr9kmSJlzaXBaLxdyAAJwTRRAAAIADzNx4RFn5xYoLD9DQFBZHBdwZRRAAAMBFMgyjoiHCbX2ayZfFUQG3xisUAADgIi3dfVzbj+YpyM9bv+vR1OxwAFwARRAAAMBFem/JXknS6G4JCg9kcVTA3VEEAQAAXITdmXn6eccxWSzS7X2bmR0OgBqgCAIAALgIHyzdJ0m6ql2MEiODzQ0GQI1QBAEAANgp+1Sxvl57SJJ0Zz8WRwXqCoogAAAAO01buV9FpTalNAlTj6QIs8MBUEMUQQAAAHYoKrXqo+X7JUl39mNxVKAuoQgCAACww3cb03Usr0gxYf66ukOc2eEAqAWKIAAAgFoyDEPvnVkcdXyfZvLz4S0VUJfwigUAAKil5XuPa1t6rgJ9vXUTi6MCdQ5FEAAAQC29/0vZKNANXePVIMjP5GgA1BZFEAAAQC3sPZav+dszJbE4KlBXUQQBAADUwgdLy0aBrmoXreZRISZHA8AeFEEAAAA1dLKgWF+dWRz1DhZHBeosiiAAAIAamrrygApLbEqOC1Pv5pFmhwPAThRBAAAANVBcatPHy/dJkib0S2JxVKAOowgCAACoge83H1FGbpGiQ/01olNjs8MBcBEoggAAAC7AMAy9f2Zx1Ft7J7I4KlDH8QoGAAC4gJVp2Uo9nKsAXy/d1DPR7HAAXCSKIAAAgAsoHwW6rku8IoJZHBWo6yiCAAAAzmNf1in9tC1DknRHX9piA/UBRRAAAMB5TFmaJsOQBrSJUstoFkcF6gOKIAAAgHPIKSjRl2vKFke989LmJkcDwFEoggAAAM7hs9UHdLrEqraxoerTgsVRgfqCIggAAKAaJVabPly6TxKLowL1DUUQAABANX7YnK6juYVqFOKvkZewOCpQn1AEAQAA/MZvF0f19/E2OSIAjkQRBAAA8Btr9p/QpkM58vPx0s09m5odDgAHowgCAAD4jfd+2StJur5LE0WG+JscDQBHowgCAAA4y/7jp/TjVhZHBeoziiAAAICzTFm6T4Yh9W8dpVYxoWaHA8AJKIIAAADOyDldoulrDkoqa4sNoH6iCAIAADjji9UHdKrYqtYxIbq0VSOzwwHgJBRBAAAAkkpZHBXwGBRBAAAAkmanHtWRnEJFBvvpmkuamB0OACeiCAIAAB7PMAy9d2Zx1HG9EhXgy+KoQH1GEQQAADzeugMntPHgSfn5eGlcr0SzwwHgZBRBAADA471/ZhRo1CWNFRXK4qhAfUcRBAAAPNrB7ALNST0qSbqDttiAR6AIAgAAHu3DZftkM6RLWzVS29gws8MB4AIUQQAAwGPlFZboi9Vli6MyCgR4DoogAADgsb5YfVD5RaVqGR2i/q2izA4HgItQBAEAAI9UarXpw2X7JEl39E2SlxeLowKegiIIAAB4pB+3ZujQidNqGOSr67qwOCrgSSiCAACAR3qfxVEBj0URBAAAPM76Aye0dv8J+Xl76ZbeLI4KeBqKIAAA4HHKR4FGdGqs6NAAk6MB4GoUQQAAwKMcPnlas88sjjqBttiAR6IIAgAAHuWjZftktRnq0yJSyY1ZHBXwRBRBAADAY+QXleqzlQckSXdeyigQ4KkogoA6yGoztDItW2uzLFqZli2rzTA7JADV4LXqfqavOai8olI1jwrW5a2jzQ4HgEl8zA4AQO3MSU3X5FlblZ5TKMlbH+9ao7jwAE0ckawhKXFmhwfgDF6r7sdqM/TB0rKGCCyOCng2RoKAOmROarru+XTdmTdVvzqaU6h7Pl2nOanpJkUG4Gy8Vt3TvK0ZOph9Wg2CfHV9l3izwwFgIoogoI6w2gxNnrVV1U2mKd82edZWptsAJimx2pSZV6gtR3L0xDepvFbd0PtL9kqSbu7ZVIF+LI4KeDKmwwF1xKq07CqfKp/NkJSeU6hVadnq3SLSdYEB9ZBhGCootir7VHHF1/FTxTrxmz+zTxXpREGJjucXKbewtGbHFq9VM2w8eFKr952Qr7dFt/ZuZnY4AExGEQTUEek5p2u0X2beuQslwFNZbYZOFhSfu6g589jx/LK/Hz9VrOJSW63PY7FIQb7eOlVsveC+vFZdq2Jx1I6NFRPG4qiAp6MIAtxcTkGJPlt9QO8s3lOj/ZfsylLv5pGK5j95ONDZXc4i07LVu2W0vE28qbywxFo2EpNfrOyCshGZ7FMlZ/4srvJ18nSJDDtmn/n5eCky2E8Rv/0K8lNEiJ8ig/3UMMhPkSFlfzYI8tOqtGyNfXfFBY994lSxHVcOexw5eVo/bC67D+sOFkcFIIogwG3tyzqlKUvTNH3tIRWc+VTZyyJd6DaC6WsP6Zv1hzW4faxu7tVUvZtHymKhAxLs5+wuZzabodzCkjPTy2r2dbrkwiMt1QkP9K22mIkIOvP92X8P9lOQn3etXz89kiIUFx6gozmF1d4XVG7SrK3afDhX/ze0raJC/e26HtTMR8v3qdRmqFfzCKU0CTc7HABugCIIcCOGYWjF3my9vyRN87dnVHxy3TY2VHf0S1KAj5ce/HxD2b5n/Vz5W7Tb+jbT5kM5WrP/hL7fnK7vN6erRVSwbu6ZqOu7xis80NeVl4N6oLzL2W/fzJd3OXtrXJcqhVBRqVUnTpXo+G9GZc6eenY8/8y2gmKdKCixq0mAr7dFEWeNxEQE+ysiyLfsz+CyPxsG+yoy2F8RwX5qEOQrX2/n9wPy9rJo4ohk3fPpOllU/Wu1b8tILd1zXF+vO6Qftx7Vo4PaaFyvRFNH1+qrU2ctjjqhX3OTowHgLiiCADdQXGrTd5uO6P0ladpyJLdi+4A2UZrQr7n6tvx1NMfPx+usT+XLxP7mU/lt6bmaunK/vll3WHuOndIz323VP+du1zWdmmhcr0R1iOeTUFxYTToSPvzFRn2x+qCyC8qmop04VaL8opo1CPitUH8fRZyZVvbbKWgNg89MPQv+9bEQfx+3HeUckhKnt8Z1Oe9rdf2BE3rq21SlHs7VxJlb9OWag3rmmhR1TWxoYuT1z1drDym3sFTNIoN0ZVsWRwVQhiIIMNGJU8WaunK/Pl6+X5l5RZKkAF8vXdclXnf0TVLL6JAqPzMkJU4Dk2O1fHemfvxlpQZd2rPK/Rnt4sL0t1Ed9H9D2+mb9Yc1dcV+bT+apy/WHNQXaw6qU3y4bu6VqBEdG9MmFue0cu/x83YklKTTJVYt3HGsynZvL0tFMVM+GtPwzOjM2cXM2ffT+PnUr1UbLvRa7dy0ob69t5+mrTqgF+ds15Yjubr+rWUa3S1efxnSVpEhTJG7WFaboSnli6P2Y3FUAL+iCAJMsDszXx8sTdOMdYdUWFLWgSo61F/j+zTTTT2aqmGw33l/3tvLop5JETq+zVDPpIhzTqEJ8ffRLb0SNa5nU63df0KfrtivHzYf1cZDOdr41SY99/023dA1Xjf3bKrmUVULLngewzC0/uBJzdp4RF+vO1SjnxnbI0ED2kRXFDORwf4KDfDhDacu/Fr19rLoll6JGpoSqxdmb9f0tYf05ZpDmrslQ48NaaPfdW/KFLmLMH9bhvYdL1B4oK9u6MriqAB+RREEuIhhGFqyO0vvL0nTz2d9ct6+cZjuvDRJwzo0dton4RaLRd2aRahbswg9NbxIX645pGmr9utg9mm9vyRN7y9JU7+WjTSuV1Nd1S5GPi64bwLuwzAMbTmSq1mbjui7jek6fLJm7djLjezUhPVuLlKjEH+9eGMnjemeoKe+3aJt6bn66zep+mL1QT17TYo6JTQwO8Q6qbwt9tgeTRXkx1seAL/iXwTAyQpLrJq54Yg+WJqm7UfzJJWtJXJVuxhN6JeknkkRLr2vITLEX/dc3kJ3X9Zci3Yd06fL92vBjkwt2Z2lJbuzFBPmr991b6qxPZoqNpw22/XZzow8zdp4RN9tSlda1qmK7UF+3hqYHKNhKXF6emaqMnKLqr0vyKKye1x6JEW4LOb6rluzCM26r68+XbFfL/24U5sO5WjUm0s1tkdT/XlQmwuOEuNXqYdztDItWz5eFo3vk2h2OADcDEUQ4CRZ+UX6dMV+fbpiv7Lyy9YDCfLz1o1d43V73yQ1axRsanxeXhYNaBOtAW2idehEgT5bdUBfrD6ojNwivTZ/l15fuFsD28VoXK9E9WkRydSmeiIt65S+23hEszYd0c6M/Irt/j5eurJdtIZ3bKwBbaIr7hWzyThvl7OJI5KZruVgPt5euq1vkq7uGKfnf9iuGesPa9rKA5q9OV3/N7StbuyawOuxBspHgYZ1jFNceKDJ0QBwNxRBgIPtOJqn95fs1f82HKlYcb5xeIDG92mm33VvqvAg92tTHd8wSH8e3FYPXtlac7cc1Scr9mtVWrbmbDmqOVuOKqlRsG7u2VQ3dI1XgyA+ia5rDp0o0Heb0vXdpiNKPfxr90Ffb4v6t47SiE6NdWW7GIX4V/0voSZdzuAc0aEBennMJRrTPUFPf7tFOzLy9JevN+uzVQf1t1EprHdzHkdzCjVr4xFJ0gQWRwVQDYogwAFsNkOLdh3TB0vS9MuurIrtnRIaaEK/JA1NiXXJ+iQXy8/HSyM6NdaITo21MyNPU1fs19frDist65T+9v02vTh3h0Z0aqxxvRLVKT7cbdsTQ8rILdT3m9I1a9MRrT9wsmK7t5dFfVs20vCOcRqcHFujorwmHQnhPD2bR+q7B/rpo2X79Mq8ndpw8KRGvr5E43ol6k8D27jlBytm+/jM4qg9mkWoY3wDs8MB4IYogoCLcLrYqhnrD+mDJWnac6zsngovizQkJVYT+iWpS9OGdbZQaB0TqsnXpOixIW317YYj+nTFfm1Nz9VXaw/pq7WHlNIkTON6JmrkJY254dhNHM8v0g+pR/XdxiNatS+7YrFdi0XqmRShEZ0aa0j7WLtaL9e0IyGcw9fbS3de2lwjOjXWc99v08yNR/Tx8v36flPZFLnru8QzRe6MguJSTS1fHPVSRoEAVI93LoAdMnML9fHy/Zq6cr9OFJRIKmtHPaZ7gm7r00wJEUEmR+g4wf4+uqlnU43tkaD1B0/q0xX79d2mdKUeztX/zdis537Ypuu7xGtcr6ZqGR1qdrgeJ6egRHO3HNWsTUe0bM9xWW2/3rnTpWkDjejUWFd3iFNMGE0u6oOYsAD9e2xn/a57gp6euUW7M/P15682lXWRG5WidnFhZodouq/XHVbO6RI1jQjSVe1izA4HgJuiCAJqIfVwjj5YkqZZm46oxFr2ZjO+YaBu75uk0d3iFRpQf6elWCwWdWnaUF2aNtRTw5I1fe1BTV15QPuPF+jDZfv04bJ96tU8QuN6JWpQcmy9W/jSneQXlWre1qP6bmO6Fu86VvFclKQOTcI1vGOchnWMU3zD+lOMo7I+LRvphwcu1QdL0/Tv+bu0Zv8JDf/PEt3aO1EPD2ytsHr8b9H52GyGPjjTEOGOvs0YsQRwTqYWQf/4xz80Y8YMbd++XYGBgerTp49eeOEFtWnTxsywgEpsNkPzt2fq/SV7tWJvdsX2bokNNaFfkga1j/W4/2gbBvvp95e10J39muuX3Vn6dMV+zd+WoRV7s7Vib7aiQv31u+4JGtujqRo3oCuTI5wutmrB9kx9t+mIFmzPVNGZphuS1CYmVCM6xWl4x8amdx2E6/j5eOkP/Vto5Jkpct9vTteUpfv03aZ0/fXqdrrmksZ1djquvRbuyFRa1imFBvjoxm4JZocDwI2ZWgQtWrRI9957r7p3767S0lI98cQTGjRokLZu3argYP4jh7lOFZXq63Vl9/vsO14gqey+iKs7xGlCvyRdwuKF8vIq6y7Wv3WUjpw8rc9XHdBnqw/qWF6R/rNgt95YuFtXnmmzfWnLRtyzUEtFpVYt3pmlWRuP6KdtGSootlY81rxRsIZ3jNPwTo3VOoZpiJ6scYNAvXFzF43ZeUyTZm7R3qxTeuiLDZq26oCevSZFbWI95/nx3i9lo0A39Wiq4Gq6HQJAOVP/hZgzZ06l7z/88ENFR0dr7dq1uuyyy6rsX1RUpKKioorvc3PLWr2WlJSopKTEucFeQPn5zY6jLnK33KXnFOqTFQf0xZpDyi0slSSFBfhoTLd43dKrqeLOLCBqdrzulreoYB/dP6C5/nBZM/20LVPTVh3UirQTmrc1Q/O2ZqhpRKB+1z1e13duoggTF3x0t7z9VonVpuV7s/X95qOaty1TeWeeg5LUpEGAhnWI1dUpsUqOC634lN9V1+LuuXNXrspb76QGmnlvb01Zuk9vLNqrVWnZuvrfv+i23k1134AW1bZAd2e1zdvW9Fwt33tc3l4W3dwj3mOfp7xO7Ufu7ONOeatNDBbDMKpbCNwUu3fvVqtWrbR582alpKRUeXzSpEmaPHlyle3Tpk1TUBBz33Fx9udLPx/x0objFtnOLAXZKMDQ5XE29Ygy5O9tcoB1UMZpaelRL606ZtFpa1lOfSyGOkca6htrU7OQss5lns5mSHtyLVqXZdHGbItOlf6alHBfQ5c0MtQl0qZE8oUayi6SvtnnpU3ZZffmhfsaGtXMps6RRr19Dn2620urj3mpc6RNt7W2XfgHANQ7BQUFuummm5STk6OwsPM3inGbIshms2nkyJE6efKklixZUu0+1Y0EJSQkKCsr64IX6mwlJSWaN2+eBg4cKF9fz7wh1V5m5s5qMzRvW6Y+XLZfa89aS6VnUkPd3jtRl7eJctv7ferSc66guFTfbz6qaasOKfXIr4t1to0N1U094jWyY5zLpq64S95sNkPrD57U96kZmpN6VMfyiyseiwj21dD2sbq6Q4y6NW3oNtMI3SV3dY2ZeVu085ie+X67DmSfliT1bh6hp4e1VcvoEJfGYY/a5C0zr0iXv7RYJVZDX93dU53iPXchWV6n9iN39nGnvOXm5qpRo0Y1KoLcZmz83nvvVWpq6jkLIEny9/eXv3/V9S18fX1NT3o5d4qlrnFl7vIKS/TlmkOasjRNh06UvTnw9bZoRMfGuqNfUp1aib0uPOfCfX11U68k3dQrSRsPntQnK/Zr1sYj2n40T0/P3KZ/zt2l67o00bheiS67v8WMvBmGoc2HczRr4xF9vyldR3IKKx4LD/TVkPaxGtGpsXo1j5CPGy+uWxeec+7IjLxd1b6x+rWO0TuL9+qNhbu1fG+2Rr65XBP6NdcDV7asE2t81SRvn63eqxKroa6JDdUtqZGLInNvvE7tR+7s4w55q8353eJfv/vuu0/fffedFi9erPj4eLPDQT12MLusnfMXqw8qv6jsXouGQb66uWeibumdyFoqLtApoYE6JTTQk8Pa6au1hzRt5QHtzTqlj5fv18fL96tHswiN652oIe3rR5ttwzC0/Wievtt0RLM2putAdkHFYyH+PhqUHKPhneLUr2VUvbheuJ8AX289cGUrjbqkiSbP2qL52zP19qI9mrnhsJ4anqwhKbF1uotcYYlVU1fulyTd2Y/FUQHUjKlFkGEYuv/++/XNN9/o559/VlIS/3jB8QzD0LoDJ/T+kjTNST2q8rUkW0QF645+Sbquc7wC/bjhx9UaBPnpzkuba0K/JC3bc1yfLN+vedsytGpftlbty1ajED+N7lbWZrsuLj67OzNf3206ou82pWt3Zn7F9gBfL13ZLkYjOjbW5W2iFODLcw+u0TQySO/f1l0/bc3QpFlbdOjEad0zdZ0ubdVIz1yToqQ62l59xrrDOlFQoviGgRrUPtbscADUEaYWQffee6+mTZumb7/9VqGhoTp69KgkKTw8XIGBrC2Ci1NitWl26lG9vyRNGw+erNh+aatGuqNfkvq3inKbey08mcViUd+WjdS3ZSMdzSnU56sP6LNVB5SRW6Q3f96jtxbt0YA20bqlV6Iua+2+92hJZSONs86M+GxL//XeJz9vL13eJkrDOzXWlW2jad0LU12VHKN+rRrpzYW79faivfplV5YGv7JYv7+sue4d0LJOfShksxl6f8leSdLtfZPc+t8HAO7F1P+J33rrLUnS5ZdfXmn7lClTdNttt7k+INQLOadL9PmqA/po2b6Key78fLw06pKy+33axprbRAPnFhseoIeuaq17B7TU/G0Z+nTFAS3ZnaUF2zO1YHum4hsG6qaeTTW6W4IahVS9P9AM6Tmn9f2mdM3alF6p2Pbxsqhfq0Ya0bGxBraPUVgA88vhPgJ8vfXIoDa6rku8Js7cokU7j+n1hbv1zfrDmjgiWQOTY+rEFLlFu45pz7FTCvH30ehuTKcHUHOmT4cDHGVf1ilNWZqm6WsPVSwqGRnsp1t6J2pcr0S3edOMC/P19tKQlDgNSYnT3mP5mrbygKavPaRDJ07rn3N26JV5O3V1hziN65WobokNXf5m7VhekWanpmvWxiNave9ExXYvi9S7RaSGd2ysIe1j1dDE9ZCAmmjWKFgf3t5dc7dk6NnvturwydP6/SdrNaBNlCaNbK/ESPeeIvf+mcVRf9c9QaF80ACgFpiTgTrNMAytTMvW+0vS9NO2DJXX1W1iQjWhX5JGXtKYey7quOZRIXpyeLIeHdxGszYe0acrD2jjwZP6dsMRfbvhiNrEhGpcr6Ya1bmJU98EnThVrDlbjuq7TUe0fM/xinvLJKl7s4Ya0amxhqbEKSqUYht1i8Vi0ZCUWF3WupHeWLhb7yzeq4U7jmnpK4t1T/8WuufyFm757+j2o7lasjtLXhZpfJ9mZocDoI6hCEKdVFxq0/ebj+i9X9K05ax1Zy5vE6U7+zVX35aRdWIqB2ouwNdbN3ZL0I3dEpR6OEefrtiv/204rB0ZeXrq2y16fvZ2jepc1ma7XZxjpjzmFpZo3pYMzdp0REt2Zan0rMqnU0IDjegYp6s7xKlxA+5hRN0X5OejPw9uWzZF7tstWrI7S6/N36Vv1h/WpJHJuqJtjNkhVlI+CjQ0Ja5ONk8BYC6KINQpJ04Va9qZ+30y88oWzg3w9dJ1XeJ1R99mahntmjVmYK6UJuF6/vqOevzqdpqx7pA+XbFfe46d0tSVBzR15QF1TWyocb2aamhKXKVPsK22spHDtVkWRaZlq3fL6Co3UhcUl+qnbZn6buMR/bzzmIpLf115vl1cmEZ0itPwDo3VNJI3XaifWkSF6JMJPfTD5qN69rutOpBdoDs+XKOByTF6eniyWxQcx/KK9O2GI5KkO2iLDcAOFEGoE/Ycy9cHS9L09bpDKiwpe1MaHeqv8X2a6aYeTbn3wkOFB/rq9r5Juq1PM63Ym61PV+zX3C1HtXb/Ca3df0LPfrdNN3aL1809ErU1PUeTZ21Vek6hJG99vGuN4sIDNHFEsi5vE62fdxzTrE1HtGBbpk6XWCvO0SIqWCM6Ndbwjo3VMjrEvIsFXMhisWhYxzhd3iZK/56/S+8vSdO8rRlavPOY7hvQUr/v31z+PuZNkftkxX4VW23q3LSBuiY2NC0OAHUXRRBMdb5P5g3D0NLdx/X+krL56eXaNw7ThH5JGt6xMYtLQlLZG7beLSLVu0WkMnML9cXqg/ps1QEdySnUfxft1X8X7a3259JzCvWHT9cpwMdLhWeN+DSNCCob8enYWG1jQ5laCY8V7O+jx69upxu6xuupb1O1Ym+2Xpq3U1+vO6TJ16Sof+sol8dUWGLV1BVli6NOYBQIgJ0ogmCaOanp1X4y//jQtiostemDJWnafjRPkmSxSFe2jdGEfknq1TyCN6U4p+iwAN1/ZSvdc3kLLdxxTB8v36dfdmWd92cKS22KC/PX8E6NNaJTY3VoEs5zDDhLq5hQfXZXL83ceETPfb9N+44XaPwHqzSkfayeGpGsJi68L+5/6w/r+KliNWkQqCEsjgrAThRBMMWc1HTd8+k6/bZJenpOoR74fEPF94G+3hrdLV639U2qs6uZwxw+3l4amByjEH+fCxZBkvTS6EvUp2UjF0QG1E0Wi0XXXNJEV7SN1qs/7dKHy/ZpzpajWrTzmO6/sqXu7Nfc6aPzhmHo/SVlDRFu69NMPt7MBgBgH4oguJzVZmjyrK1VCqCzeVmkRwe30c09EhUexNoPsF9mXmGN9juWX+TkSID6ITTAV08NT9aN3eL19P+2aNW+bP1zzg59tfaQnr0mRX2d+GHC4l1Z2pWZr2A/b43pkeC08wCo/yiC4HCFJVZl5RcpK79YWXlFZ/5e9v2xvCLtOZZ/ZgrcudkMqXNCQwogXLTo0ACH7gegTNvYMH1xdy99s/6w/v7DNu09dko3v7dSwzrG6alhyYoNd/xrqnwUaHT3BIWxOCqAi0ARhBopLLHqWN6vxUxWftFZ3xcpK+/Mtvwi5RWWOuScNf0EHzifHkkRigsP0NGcwmpHHy2SYsMD1CMpwtWhAXWexWLRdV3idWW7GL0yb6c+Xr5P329K18/bM/XgVa10e98k+TpoytrOjDwt3nlMXhbp9j40RABwcSiCPNjpYmtF4ZKVV/5n8VkjN7+O3uQX1a6w8fP2UqMQPzUK9VejEP+yv4eU/f1kQbH+vWD3BY/BJ/NwBG8viyaOSNY9n66TRapUCJW3Ppg4IrnKekEAai480FeTRrbXjd3i9dT/UrXuwEn9/Yftmr7mkJ65JkW9W0Re9Dk+ODMKNCg5lnW6AFw0iiAHqMkCjK5SUFyqrLxiHcsv1LHfFjRnjdZk5RXpVLH1wgc8i5+Pl6J+U9BEhfr/ptjxV1SIv8ICfc7ZXctqMzR97SE+mYfLDEmJ01vjupzVjbBM7Jl1goakxJkYHVB/tG8crq/+0EdfrTuk52dv167MfI19d4VGXdJYT1zdTtFh9n24lZVfpBnrD0uS7ryUUSAAF48i6CKdq82zI99YnSoqrTT17NhZ99r8dopaQS0LG38fr7LiJdRfUSF+Z4qas7/KCpyoUH+F+p+7sKkNPpmHGYakxGlgcqyW787Uj7+s1KBLe5r6gQVQX3l5WTS6W4IGJcfoXz/u0NSVB/S/DUf007ZMPTywtcb3Tqx1V7epKw6ouNSmTvHhLI4KwCEogi7Cudo8H80p1D2frtNb47pUWwgZhqH8otKKwqWioDkz9ey3ozdnr15fEwG+XmeN0pSPzvxmtObMCE6Igwqb2uKTeZjB28uinkkROr7NUM+kCAogwIkaBPnpb6M6aEy3pnry21RtPHhSz363VdPXHNSzo1LUvVnNRvuLSqz6ZMU+SdKES5uzhhcAh6AIstP52jyXb3vsq03afDhH2aeKK01NO5ZXpKKzVqeviUBf71+nnp0ZuSkvbiqN3oT6K9jPu078J8En8wBQ/3WID9c39/TRF2sO6oU527X9aJ5ufHu5ruvSRI8PbaeoUP/z/vyszUeVlV+suPAADU1hcVQAjkERZKdVadkXbPOcW1iqNxbuOefjwX7e1TYOKC9qokJ/3RbsXz9/VXwyDwD1n5eXRWN7NNWQ9rH659zt+nz1Qc1Yd1jztmbo0UFtNK5XYrX//huG9OGy/ZLKFkd1VKc5AKif76xdoKbtm/u1bKTuzSLU6KyCJirEX41C/RTkR/oBAJ6jYbCf/nFdR43ulqCnvk1V6uFcTZy5RV+uOahnrkmpuN+nvOHQ9we8tCMjX4G+Xvpdj6YmRw+gPuFduJ1q2r753gEtHdIaFACA+qJz04b69t5+mrbqgF6cs11bjuTq+reWaXS3eHVvFqGX5+08M9uibOTHYrFo+Z4s7hcF4DCMK9upfAHGc03eskiKo80zAADV8vay6JZeiVr46OW6sWu8JOnLNYf05682VZluXlBs1T2frtOc1HQzQgVQD1EE2am8zbOkKoUQbZ4BAKiZyBB/vXhjJ315dy/5XOD/zMmztspqq64lEQDUDkXQRShv8xwbXnlqXGx4wDnbYwMAgKqsNqn0PAWOISk9p1Cr0rJdFxSAeot7gi4SbZ4BALh4NW04VNP9AOB8KIIcgDbPAABcnJo2HKrpfgBwPkyHAwAApqPhEABXoggCAACmo+EQAFeiCAIAAG6BhkMAXIV7ggAAgNug4RAAV6AIAgAAboWGQwCcjelwAAAAADwKRRAAAAAAj0IRBAAAAMCjUAQBAAAA8CgUQQAAAAA8CkUQAAAAAI9CEQQAAADAo1AEAQAAAPAoFEEAAAAAPApFEAAAAACPQhEEAAAAwKNQBAEAAADwKBRBAAAAADyKj9kBXAzDMCRJubm5JkcilZSUqKCgQLm5ufL19TU7nDqF3NmHvNmHvNmP3NmHvNmHvNmHvNmP3NnHnfJWXhOU1wjnU6eLoLy8PElSQkKCyZEAAAAAcAd5eXkKDw8/7z4Woyalkpuy2Ww6cuSIQkNDZbFYTI0lNzdXCQkJOnjwoMLCwkyNpa4hd/Yhb/Yhb/Yjd/Yhb/Yhb/Yhb/Yjd/Zxp7wZhqG8vDw1btxYXl7nv+unTo8EeXl5KT4+3uwwKgkLCzP9CVBXkTv7kDf7kDf7kTv7kDf7kDf7kDf7kTv7uEveLjQCVI7GCAAAAAA8CkUQAAAAAI9CEeQg/v7+mjhxovz9/c0Opc4hd/Yhb/Yhb/Yjd/Yhb/Yhb/Yhb/Yjd/apq3mr040RAAAAAKC2GAkCAAAA4FEoggAAAAB4FIogAAAAAB6FIggAAACAR6EIOss//vEPde/eXaGhoYqOjtaoUaO0Y8eOSvsUFhbq3nvvVWRkpEJCQnT99dcrIyOj0j4PPPCAunbtKn9/f11yySXnPefu3bsVGhqqBg0aOPhqXMdVedu3b58sFkuVrxUrVjjz8pzGlc83wzD0r3/9S61bt5a/v7+aNGmi5557zlmX5nSuyt2kSZOqfc4FBwc78/KcxpXPublz56pXr14KDQ1VVFSUrr/+eu3bt89JV+Zcrszbl19+qUsuuURBQUFKTEzUiy++6KzLcglH5G7jxo0aO3asEhISFBgYqHbt2um1116rcq6ff/5ZXbp0kb+/v1q2bKkPP/zQ2ZfnNK7KW3p6um666Sa1bt1aXl5eeuihh1xxeU7jqrzNmDFDAwcOVFRUlMLCwtS7d2/NnTvXJdfoDK7K25IlS9S3b19FRkYqMDBQbdu21SuvvOKSa6wORdBZFi1apHvvvVcrVqzQvHnzVFJSokGDBunUqVMV+zz88MOaNWuWpk+frkWLFunIkSO67rrrqhzrjjvu0JgxY857vpKSEo0dO1aXXnqpw6/FlVydt59++knp6ekVX127dnX4NbmCK/P24IMP6r333tO//vUvbd++XTNnzlSPHj2ccl2u4KrcPfroo5Wea+np6UpOTtaNN97otGtzJlflLS0tTddcc42uuOIKbdiwQXPnzlVWVla1x6kLXJW32bNn6+abb9Yf/vAHpaam6s0339Qrr7yi119/3WnX5myOyN3atWsVHR2tTz/9VFu2bNFf//pXPf7445XykpaWpmHDhmnAgAHasGGDHnroId1555119o2pq/JWVFSkqKgoPfnkk+rUqZNLr9EZXJW3xYsXa+DAgfrhhx+0du1aDRgwQCNGjND69etder2O4qq8BQcH67777tPixYu1bds2Pfnkk3ryySf1zjvvuPR6Kxg4p8zMTEOSsWjRIsMwDOPkyZOGr6+vMX369Ip9tm3bZkgyli9fXuXnJ06caHTq1Omcx3/ssceMcePGGVOmTDHCw8MdHb5pnJW3tLQ0Q5Kxfv16Z4VuKmflbevWrYaPj4+xfft2p8VuNme/Vstt2LDBkGQsXrzYYbGbyVl5mz59uuHj42NYrdaKbTNnzjQsFotRXFzs+AtxMWflbezYscYNN9xQadu///1vIz4+3rDZbI69CJNcbO7K/fGPfzQGDBhQ8f1jjz1mtG/fvtI+Y8aMMQYPHuzgKzCHs/J2tv79+xsPPvigQ+M2myvyVi45OdmYPHmyYwI3mSvzdu211xrjxo1zTOC1xEjQeeTk5EiSIiIiJJVVuSUlJbrqqqsq9mnbtq2aNm2q5cuX1+rYCxYs0PTp0/XGG284LmA34cy8SdLIkSMVHR2tfv36aebMmY4J2g04K2+zZs1S8+bN9d133ykpKUnNmjXTnXfeqezsbMdegImc/Zwr995776l169Z1fvS2nLPy1rVrV3l5eWnKlCmyWq3KycnRJ598oquuukq+vr6OvQgTOCtvRUVFCggIqLQtMDBQhw4d0v79+x0QufkclbucnJyKY0jS8uXLKx1DkgYPHnxRr3d34qy81XeuypvNZlNeXl69ya2r8rZ+/XotW7ZM/fv3d1DktUMRdA42m00PPfSQ+vbtq5SUFEnS0aNH5efnV+X+nZiYGB09erTGxz5+/Lhuu+02ffjhhwoLC3Nk2KZzZt5CQkL00ksvafr06fr+++/Vr18/jRo1ql4UQs7M2969e7V//35Nnz5dH3/8sT788EOtXbtWN9xwgyMvwTTOzN3ZCgsLNXXqVE2YMOFiQ3YLzsxbUlKSfvzxRz3xxBPy9/dXgwYNdOjQIX355ZeOvARTODNvgwcP1owZMzR//nzZbDbt3LlTL730kqSyezfqOkflbtmyZfriiy/0+9//vmLb0aNHFRMTU+UYubm5On36tGMvxMWcmbf6zJV5+9e//qX8/HyNHj3aYfGbxRV5i4+Pl7+/v7p166Z7771Xd955p8OvoyZ8TDlrHXDvvfcqNTVVS5Yscfix77rrLt1000267LLLHH5sszkzb40aNdIjjzxS8X337t115MgRvfjiixo5cqTDz+dKzsybzWZTUVGRPv74Y7Vu3VqS9P7776tr167asWOH2rRp4/BzupIzc3e2b775Rnl5eRo/frxTz+Mqzszb0aNHddddd2n8+PEaO3as8vLy9PTTT+uGG27QvHnzZLFYHH5OV3H2/w179uzR8OHDVVJSorCwMD344IOaNGmSvLzq/meWjshdamqqrrnmGk2cOFGDBg1yYHTui7zZx1V5mzZtmiZPnqxvv/1W0dHRdp/LXbgib7/88ovy8/O1YsUK/d///Z9atmypsWPHXkzYdqn7/6o6wX333afvvvtOCxcuVHx8fMX22NhYFRcX6+TJk5X2z8jIUGxsbI2Pv2DBAv3rX/+Sj4+PfHx8NGHCBOXk5MjHx0cffPCBoy7D5Zydt+r07NlTu3fvvqhjmM3ZeYuLi5OPj09FASRJ7dq1kyQdOHDg4oI3mSufc++9956GDx9e5dPmusjZeXvjjTcUHh6uf/7zn+rcubMuu+wyffrpp5o/f75WrlzpqMtwOWfnzWKx6IUXXlB+fr7279+vo0ePVjQwad68uUOuwSyOyN3WrVt15ZVX6ve//72efPLJSo/FxsZW6caXkZGhsLAwBQYGOvZiXMjZeauvXJW3zz//XHfeeae+/PLLKtMx6yJX5S0pKUkdOnTQXXfdpYcffliTJk1y9KXUCEXQWQzD0H333advvvlGCxYsUFJSUqXHu3btKl9fX82fP79i244dO3TgwAH17t27xudZvny5NmzYUPH1zDPPKDQ0VBs2bNC1117rsOtxFVflrTobNmxQXFzcRR3DLK7KW9++fVVaWqo9e/ZUbNu5c6ckKTEx8SKvwhyufs6lpaVp4cKFdX4qnKvyVlBQUGXkwtvbW1LZyGRd4+rnm7e3t5o0aSI/Pz999tln6t27t6Kioi76OszgqNxt2bJFAwYM0Pjx46tt79+7d+9Kx5CkefPmXfT/MWZxVd7qG1fm7bPPPtPtt9+uzz77TMOGDXPOBbmImc+38tkqpjClHYObuueee4zw8HDj559/NtLT0yu+CgoKKvb5wx/+YDRt2tRYsGCBsWbNGqN3795G7969Kx1n165dxvr16427777baN26tbF+/Xpj/fr1RlFRUbXnrevd4VyVtw8//NCYNm2asW3bNmPbtm3Gc889Z3h5eRkffPCBS6/XUVyVN6vVanTp0sW47LLLjHXr1hlr1qwxevbsaQwcONCl1+tIrn6tPvnkk0bjxo2N0tJSl1yfs7gqb/PnzzcsFosxefJkY+fOncbatWuNwYMHG4mJiZXOVVe4Km/Hjh0z3nrrLWPbtm3G+vXrjQceeMAICAgwVq5c6dLrdSRH5G7z5s1GVFSUMW7cuErHyMzMrNhn7969RlBQkPHnP//Z2LZtm/HGG28Y3t7expw5c1x6vY7iqrwZhlHxPOzatatx0003GevXrze2bNnismt1JFflberUqYaPj4/xxhtvVNrn5MmTLr1eR3FV3l5//XVj5syZxs6dO42dO3ca7733nhEaGmr89a9/den1lqMIOoukar+mTJlSsc/p06eNP/7xj0bDhg2NoKAg49prrzXS09MrHad///7VHictLa3a89b1IshVefvwww+Ndu3aGUFBQUZYWJjRo0ePSu0a6xpXPt8OHz5sXHfddUZISIgRExNj3Hbbbcbx48dddKWO58rcWa1WIz4+3njiiSdcdHXO48q8ffbZZ0bnzp2N4OBgIyoqyhg5cqSxbds2F12pY7kqb8eOHTN69eplBAcHG0FBQcaVV15prFixwoVX6niOyN3EiROrPUZiYmKlcy1cuNC45JJLDD8/P6N58+aVzlHXuDJvNdmnrnBV3s71Wh4/frzrLtaBXJW3f//730b79u0r3sd17tzZePPNNystp+BKFsMwDAEAAACAh+CeIAAAAAAehSIIAAAAgEehCAIAAADgUSiCAAAAAHgUiiAAAAAAHoUiCAAAAIBHoQgCAAAA4FEoggAAAAB4FIogAAAAAB6FIggA4DYMw9BVV12lwYMHV3nszTffVIMGDXTo0CETIgMA1CcUQQAAt2GxWDRlyhStXLlS//3vfyu2p6Wl6bHHHtN//vMfxcfHO/ScJSUlDj0eAMD9UQQBANxKQkKCXnvtNT366KNKS0uTYRiaMGGCBg0apM6dO2vo0KEKCQlRTEyMbrnlFmVlZVX87Jw5c9SvXz81aNBAkZGRGj58uPbs2VPx+L59+2SxWPTFF1+of//+CggI0NSpU824TACAiSyGYRhmBwEAwG+NGjVKOTk5uu666/Tss89qy5Ytat++ve68807deuutOn36tP7yl7+otLRUCxYskCR9/fXXslgs6tixo/Lz8/X0009r37592rBhg7y8vLRv3z4lJSWpWbNmeumll9S5c2cFBAQoLi7O5KsFALgSRRAAwC1lZmaqffv2ys7O1tdff63U1FT98ssvmjt3bsU+hw4dUkJCgnbs2KHWrVtXOUZWVpaioqK0efNmpaSkVBRBr776qh588EFXXg4AwI0wHQ4A4Jaio6N19913q127dho1apQ2btyohQsXKiQkpOKrbdu2klQx5W3Xrl0aO3asmjdvrrCwMDVr1kySdODAgUrH7tatm0uvBQDgXnzMDgAAgHPx8fGRj0/Zf1X5+fkaMWKEXnjhhSr7lU9nGzFihBITE/Xuu++qcePGstlsSklJUXFxcaX9g4ODnR88AMBtUQQBAOqELl266Ouvv1azZs0qCqOzHT9+XDt27NC7776rSy+9VJK0ZMkSV4cJAKgDmA4HAKgT7r33XmVnZ2vs2LFavXq19uzZo7lz5+r222+X1WpVw4YNFRkZqXfeeUe7d+/WggUL9Mgjj5gdNgDADVEEAQDqhMaNG2vp0qWyWq0aNGiQOnTooIceekgNGjSQl5eXvLy89Pnnn2vt2rVKSUnRww8/rBdffNHssAEAbojucAAAAAA8CiNBAAAAADwKRRAAAAAAj0IRBAAAAMCjUAQBAAAA8CgUQQAAAAA8CkUQAAAAAI9CEQQAAADAo1AEAQAAAPAoFEEAAAAAPApFEAAAAACPQhEEAAAAwKP8P6KQ14ErFH3sAAAAAElFTkSuQmCC", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import matplotlib.pyplot as plt\n", - "import pandas as pd\n", - "\n", - "# Read the CSV file\n", - "df = pd.read_csv(\"/tmp/tmpco0s0o4_/LOdZoVp1inflation.csv\")\n", - "\n", - "# Extract the year and inflation rate from the CSV file\n", - "df[\"Year\"] = pd.to_datetime(df[\"Year\"], format=\"%Y\")\n", - "df = df.rename(\n", - " columns={\n", - " \"Jan\": \"Jan Rate\",\n", - " \"Feb\": \"Feb Rate\",\n", - " \"Mar\": \"Mar Rate\",\n", - " \"Apr\": \"Apr Rate\",\n", - " \"May\": \"May Rate\",\n", - " \"Jun\": \"Jun Rate\",\n", - " \"Jul\": \"Jul Rate\",\n", - " \"Aug\": \"Aug Rate\",\n", - " \"Sep\": \"Sep Rate\",\n", - " \"Oct\": \"Oct Rate\",\n", - " \"Nov\": \"Nov Rate\",\n", - " \"Dec\": \"Dec Rate\",\n", - " }\n", - ")\n", - "\n", - "# Calculate the average yearly inflation rate\n", - "df[\"Yearly Inflation\"] = df[\n", - " [\n", - " \"Jan Rate\",\n", - " \"Feb Rate\",\n", - " \"Mar Rate\",\n", - " \"Apr Rate\",\n", - " \"May Rate\",\n", - " \"Jun Rate\",\n", - " \"Jul Rate\",\n", - " \"Aug Rate\",\n", - " \"Sep Rate\",\n", - " \"Oct Rate\",\n", - " \"Nov Rate\",\n", - " \"Dec Rate\",\n", - " ]\n", - "].mean(axis=1)\n", - "\n", - "# Plot the average yearly inflation rate as a time series\n", - "plt.figure(figsize=(10, 6))\n", - "plt.plot(df[\"Year\"], df[\"Yearly Inflation\"], marker=\"o\")\n", - "plt.title(\"Average Yearly Inflation Rate\")\n", - "plt.xlabel(\"Year\")\n", - "plt.ylabel(\"Inflation Rate (%)\")\n", - "plt.grid(True)\n", - "plt.show()\n" - ] - }, - { - "cell_type": "markdown", - "id": "FJ85DUhgBZd7", - "metadata": { - "id": "FJ85DUhgBZd7" - }, - "source": [ - "## 3. Llama Stack Agent Evaluations\n" - ] - }, - { - "cell_type": "markdown", - "id": "ydeBDpDT5VHd", - "metadata": { - "id": "ydeBDpDT5VHd" - }, - "source": [ - "#### 3.1. Online Evaluation Dataset Collection Using Telemetry\n", - "\n", - "- Llama Stack offers built-in telemetry to collect traces and data about your agentic application.\n", - "- In this example, we will show how to build an Agent with Llama Stack, and query the agent's traces into an online dataset that can be used for evaluation. " - ] - }, - { - "cell_type": "markdown", - "id": "_JueJAKyJR5m", - "metadata": { - "id": "_JueJAKyJR5m" - }, - "source": [ - "##### 🚧 Patches 🚧\n", - "- The following cells are temporary patches to get `telemetry` working." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "klPkK1t7CzIY", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "klPkK1t7CzIY", - "outputId": "ab0c1490-7fa6-446c-8e35-7b42f57e8a04" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Found existing installation: llama_stack 0.0.61\n", - "Uninstalling llama_stack-0.0.61:\n", - " Would remove:\n", - " /usr/local/bin/install-wheel-from-presigned\n", - " /usr/local/bin/llama\n", - " /usr/local/lib/python3.10/dist-packages/llama_stack-0.0.61.dist-info/*\n", - " /usr/local/lib/python3.10/dist-packages/llama_stack/*\n", - "Proceed (Y/n)? Y\n", - " Successfully uninstalled llama_stack-0.0.61\n", - "Collecting git+https://github.com/meta-llama/llama-stack.git@main\n", - " Cloning https://github.com/meta-llama/llama-stack.git (to revision main) to /tmp/pip-req-build-oryyzdm1\n", - " Running command git clone --filter=blob:none --quiet https://github.com/meta-llama/llama-stack.git /tmp/pip-req-build-oryyzdm1\n", - " Resolved https://github.com/meta-llama/llama-stack.git to commit 53b3a1e345c46d7d37c1af3d675092a4cbfe85f9\n", - " Running command git submodule update --init --recursive -q\n", - " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", - " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", - " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: blobfile in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (3.0.0)\n", - "Requirement already satisfied: fire in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (0.7.0)\n", - "Requirement already satisfied: httpx in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (0.28.1)\n", - "Requirement already satisfied: huggingface-hub in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (0.26.5)\n", - "Requirement already satisfied: llama-models>=0.0.61 in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (0.0.61)\n", - "Requirement already satisfied: llama-stack-client>=0.0.61 in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (0.0.61)\n", - "Requirement already satisfied: prompt-toolkit in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (3.0.48)\n", - "Requirement already satisfied: python-dotenv in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (1.0.1)\n", - "Requirement already satisfied: pydantic>=2 in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (2.10.3)\n", - "Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (2.32.3)\n", - "Requirement already satisfied: rich in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (13.9.4)\n", - "Requirement already satisfied: setuptools in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (75.1.0)\n", - "Requirement already satisfied: termcolor in /usr/local/lib/python3.10/dist-packages (from llama_stack==0.0.61) (2.5.0)\n", - "Requirement already satisfied: PyYAML in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama_stack==0.0.61) (6.0.2)\n", - "Requirement already satisfied: jinja2 in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama_stack==0.0.61) (3.1.4)\n", - "Requirement already satisfied: tiktoken in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama_stack==0.0.61) (0.8.0)\n", - "Requirement already satisfied: Pillow in /usr/local/lib/python3.10/dist-packages (from llama-models>=0.0.61->llama_stack==0.0.61) (10.4.0)\n", - "Requirement already satisfied: anyio<5,>=3.5.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama_stack==0.0.61) (3.7.1)\n", - "Requirement already satisfied: click in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama_stack==0.0.61) (8.1.7)\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama_stack==0.0.61) (1.9.0)\n", - "Requirement already satisfied: pandas in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama_stack==0.0.61) (2.2.2)\n", - "Requirement already satisfied: pyaml in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama_stack==0.0.61) (24.12.1)\n", - "Requirement already satisfied: sniffio in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama_stack==0.0.61) (1.3.1)\n", - "Requirement already satisfied: tqdm in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama_stack==0.0.61) (4.66.6)\n", - "Requirement already satisfied: typing-extensions<5,>=4.7 in /usr/local/lib/python3.10/dist-packages (from llama-stack-client>=0.0.61->llama_stack==0.0.61) (4.12.2)\n", - "Requirement already satisfied: certifi in /usr/local/lib/python3.10/dist-packages (from httpx->llama_stack==0.0.61) (2024.8.30)\n", - "Requirement already satisfied: httpcore==1.* in /usr/local/lib/python3.10/dist-packages (from httpx->llama_stack==0.0.61) (1.0.7)\n", - "Requirement already satisfied: idna in /usr/local/lib/python3.10/dist-packages (from httpx->llama_stack==0.0.61) (3.10)\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /usr/local/lib/python3.10/dist-packages (from httpcore==1.*->httpx->llama_stack==0.0.61) (0.14.0)\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama_stack==0.0.61) (0.7.0)\n", - "Requirement already satisfied: pydantic-core==2.27.1 in /usr/local/lib/python3.10/dist-packages (from pydantic>=2->llama_stack==0.0.61) (2.27.1)\n", - "Requirement already satisfied: pycryptodomex>=3.8 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama_stack==0.0.61) (3.21.0)\n", - "Requirement already satisfied: urllib3<3,>=1.25.3 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama_stack==0.0.61) (2.2.3)\n", - "Requirement already satisfied: lxml>=4.9 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama_stack==0.0.61) (5.3.0)\n", - "Requirement already satisfied: filelock>=3.0 in /usr/local/lib/python3.10/dist-packages (from blobfile->llama_stack==0.0.61) (3.16.1)\n", - "Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama_stack==0.0.61) (2024.9.0)\n", - "Requirement already satisfied: packaging>=20.9 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub->llama_stack==0.0.61) (24.2)\n", - "Requirement already satisfied: wcwidth in /usr/local/lib/python3.10/dist-packages (from prompt-toolkit->llama_stack==0.0.61) (0.2.13)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->llama_stack==0.0.61) (3.4.0)\n", - "Requirement already satisfied: markdown-it-py>=2.2.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama_stack==0.0.61) (3.0.0)\n", - "Requirement already satisfied: pygments<3.0.0,>=2.13.0 in /usr/local/lib/python3.10/dist-packages (from rich->llama_stack==0.0.61) (2.18.0)\n", - "Requirement already satisfied: exceptiongroup in /usr/local/lib/python3.10/dist-packages (from anyio<5,>=3.5.0->llama-stack-client>=0.0.61->llama_stack==0.0.61) (1.2.2)\n", - "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0->rich->llama_stack==0.0.61) (0.1.2)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /usr/local/lib/python3.10/dist-packages (from jinja2->llama-models>=0.0.61->llama_stack==0.0.61) (3.0.2)\n", - "Requirement already satisfied: numpy>=1.22.4 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama_stack==0.0.61) (1.26.4)\n", - "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama_stack==0.0.61) (2.8.2)\n", - "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama_stack==0.0.61) (2024.2)\n", - "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.10/dist-packages (from pandas->llama-stack-client>=0.0.61->llama_stack==0.0.61) (2024.2)\n", - "Requirement already satisfied: regex>=2022.1.18 in /usr/local/lib/python3.10/dist-packages (from tiktoken->llama-models>=0.0.61->llama_stack==0.0.61) (2024.9.11)\n", - "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.10/dist-packages (from python-dateutil>=2.8.2->pandas->llama-stack-client>=0.0.61->llama_stack==0.0.61) (1.17.0)\n", - "Building wheels for collected packages: llama_stack\n", - " Building wheel for llama_stack (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for llama_stack: filename=llama_stack-0.0.61-py3-none-any.whl size=464145 sha256=da71747aceef9aec43553f66c43095486d1a920e47bb0e47e2729a8e4328fff6\n", - " Stored in directory: /tmp/pip-ephem-wheel-cache-jquw5j7f/wheels/74/e4/3b/079983408fa9323c1f2807e404ee78b468c74bec381eb70d4f\n", - "Successfully built llama_stack\n", - "Installing collected packages: llama_stack\n", - "Successfully installed llama_stack-0.0.61\n" - ] - }, - { - "data": { - "application/vnd.colab-display-data+json": { - "id": "7701cb0c982f4250a46721fededf9647", - "pip_warning": { - "packages": [ - "llama_stack" - ] - } - } - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# need to install on latest main\n", - "!pip uninstall llama-stack\n", - "!pip install git+https://github.com/meta-llama/llama-stack.git@main" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9jJ75JlnETTH", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "9jJ75JlnETTH", - "outputId": "76bd3912-f814-428c-88e1-c1113af77856" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Removed handler StreamHandler from root logger\n" - ] - } - ], - "source": [ - "# disable logging for clean server logs\n", - "import logging\n", - "\n", - "\n", - "def remove_root_handlers():\n", - " root_logger = logging.getLogger()\n", - " for handler in root_logger.handlers[:]:\n", - " root_logger.removeHandler(handler)\n", - " print(f\"Removed handler {handler.__class__.__name__} from root logger\")\n", - "\n", - "\n", - "remove_root_handlers()\n" - ] - }, - { - "cell_type": "markdown", - "id": "_t_tcWq0JcJ4", - "metadata": { - "id": "_t_tcWq0JcJ4" - }, - "source": [ - "##### 3.1.1. Building a Search Agent" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4iCO59kP20Zs", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "4iCO59kP20Zs", - "outputId": "f6179de6-054d-4452-a893-8d9b64c5a0d1" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "inference> Let me check the latest sports news.\n", - "inference> bravy_search.call(query=\"Bill Cosby South Park episode\")\n", - "CustomTool> Unknown tool `bravy_search` was called.\n", - "inference> brave_search.call(query=\"Andrew Tate kickboxing name\")\n", - "tool_execution> Tool:brave_search Args:{'query': 'Andrew Tate kickboxing name'}\n", - "tool_execution> Tool:brave_search Response:{\"query\": \"Andrew Tate kickboxing name\", \"top_k\": [{\"title\": \"Andrew Tate kickboxing record: How many championships ... - FirstSportz\", \"url\": \"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\", \"content\": \"Andrew Tate's Kickboxing career. During his kickboxing career, he used the nickname \\\"King Cobra,\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\", \"score\": 0.9996244, \"raw_content\": null}, {\"title\": \"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\", \"url\": \"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\", \"content\": \"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\", \"score\": 0.99909246, \"raw_content\": null}, {\"title\": \"Who is Andrew Tate? MMA, kickboxing record and controversies of fighter ...\", \"url\": \"https://www.sportingnews.com/us/kickboxing/news/andrew-tate-mma-kickboxing-record-controversies/u50waalc9cfz7krjg9wnyb7p\", \"content\": \"Andrew Tate kickboxing record After launching his career as a 20-year-old in 2007, Tate built a formidable kickboxing record that included 76 wins across 85 fights in more than 13 years in the ring.\", \"score\": 0.9976586, \"raw_content\": null}, {\"title\": \"About Andrew Tate: A Journey from Champion to Controversy\", \"url\": \"https://reachmorpheus.com/andrew-tate/\", \"content\": \"Andrew Tate's kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\", \"score\": 0.99701905, \"raw_content\": null}, {\"title\": \"Andrew Tate Bio, Wiki, Net Worth, Age, Family, MMA Career - Next Biography\", \"url\": \"https://www.nextbiography.com/andrew-tate/\", \"content\": \"Andrew Tate Age. Andrew Tate is 36 years old as of 2023, born on December 1, 1986, in Washington, DC. By his mid-thirties, Andrew Tate has become an esteemed figure in the world of kickboxing, showcasing remarkable expertise and experience in the sport. Early Life of Andrew Tate. Andrew Tate was born on 01 December 1986 to an African-American\", \"score\": 0.99368566, \"raw_content\": null}]}\n", - "shield_call> No Violation\n", - "inference> Andrew Tate's kickboxing name is \"King Cobra.\"\n" - ] - } - ], - "source": [ - "from google.colab import userdata\n", - "from llama_stack_client.lib.agents.agent import Agent\n", - "from llama_stack_client.lib.agents.event_logger import EventLogger\n", - "from llama_stack_client.types.agent_create_params import AgentConfig\n", - "\n", - "agent_config = AgentConfig(\n", - " model=\"meta-llama/Llama-3.1-405B-Instruct\",\n", - " instructions=\"You are a helpful assistant. Use search tool to answer the questions. \",\n", - " tools=(\n", - " [\n", - " {\n", - " \"type\": \"brave_search\",\n", - " \"engine\": \"tavily\",\n", - " \"api_key\": userdata.get(\"TAVILY_SEARCH_API_KEY\"),\n", - " }\n", - " ]\n", - " ),\n", - " input_shields=[],\n", - " output_shields=[],\n", - " enable_session_persistence=False,\n", - ")\n", - "agent = Agent(client, agent_config)\n", - "user_prompts = [\n", - " \"Which teams played in the NBA western conference finals of 2024\",\n", - " \"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\n", - " \"What is the British-American kickboxer Andrew Tate's kickboxing name?\",\n", - "]\n", - "\n", - "session_id = agent.create_session(\"test-session\")\n", - "\n", - "for prompt in user_prompts:\n", - " response = agent.create_turn(\n", - " messages=[\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": prompt,\n", - " }\n", - " ],\n", - " session_id=session_id,\n", - " )\n", - "\n", - " for log in EventLogger().log(response):\n", - " log.print()\n" - ] - }, - { - "cell_type": "markdown", - "id": "ekOS2kM4P0LM", - "metadata": { - "id": "ekOS2kM4P0LM" - }, - "source": [ - "##### 3.1.2 Query Telemetry" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "agkWgToGAsuA", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 760 - }, - "id": "agkWgToGAsuA", - "outputId": "647cd5d2-7610-4fd6-ef66-c3f2f782a1b0" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Getting traces for session_id=ac651ce8-2281-47f2-8814-ef947c066e40\n" - ] - }, - { - "data": { - "text/html": [ - "
[\n",
-              "{\n",
-              "│   │   'input': [\n",
-              "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}'\n",
-              "│   │   ],\n",
-              "│   │   'output': 'content: Let me check the latest sports news. tool_calls: []'\n",
-              "},\n",
-              "{\n",
-              "│   │   'input': [\n",
-              "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}'\n",
-              "│   │   ],\n",
-              "│   │   'output': \"content:  tool_calls: [ToolCall(call_id='19bd3554-e670-4856-89d0-c63f5b016245', tool_name='bravy_search', arguments={'query': 'Bill Cosby South Park episode'})]\"\n",
-              "},\n",
-              "{\n",
-              "│   │   'input': [\n",
-              "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"19bd3554-e670-4856-89d0-c63f5b016245\",\"tool_name\":\"bravy_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}'\n",
-              "│   │   ],\n",
-              "│   │   'output': \"content:  tool_calls: [ToolCall(call_id='526045a7-5f51-40fb-ba97-5ad29610e511', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\"\n",
-              "},\n",
-              "{\n",
-              "│   │   'input': '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"526045a7-5f51-40fb-ba97-5ad29610e511\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n",
-              "│   │   'output': '{\"role\":\"ipython\",\"call_id\":\"526045a7-5f51-40fb-ba97-5ad29610e511\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.9996244, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.99909246, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Who is Andrew Tate? MMA, kickboxing record and controversies of fighter ...\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportingnews.com/us/kickboxing/news/andrew-tate-mma-kickboxing-record-controversies/u50waalc9cfz7krjg9wnyb7p\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate kickboxing record After launching his career as a 20-year-old in 2007, Tate built a formidable kickboxing record that included 76 wins across 85 fights in more than 13 years in the ring.\\\\\", \\\\\"score\\\\\": 0.9976586, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.99701905, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate Bio, Wiki, Net Worth, Age, Family, MMA Career - Next Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nextbiography.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age. Andrew Tate is 36 years old as of 2023, born on December 1, 1986, in Washington, DC. By his mid-thirties, Andrew Tate has become an esteemed figure in the world of kickboxing, showcasing remarkable expertise and experience in the sport. Early Life of Andrew Tate. Andrew Tate was born on 01 December 1986 to an African-American\\\\\", \\\\\"score\\\\\": 0.99368566, \\\\\"raw_content\\\\\": null}]}\"}'\n",
-              "},\n",
-              "{\n",
-              "│   │   'input': [\n",
-              "│   │   │   '{\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"19bd3554-e670-4856-89d0-c63f5b016245\",\"tool_name\":\"bravy_search\",\"arguments\":{\"query\":\"Bill Cosby South Park episode\"}}]}',\n",
-              "│   │   │   '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}',\n",
-              "│   │   │   '{\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":[{\"call_id\":\"526045a7-5f51-40fb-ba97-5ad29610e511\",\"tool_name\":\"brave_search\",\"arguments\":{\"query\":\"Andrew Tate kickboxing name\"}}]}',\n",
-              "│   │   │   '{\"role\":\"ipython\",\"call_id\":\"526045a7-5f51-40fb-ba97-5ad29610e511\",\"tool_name\":\"brave_search\",\"content\":\"{\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": [{\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.9996244, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.99909246, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Who is Andrew Tate? MMA, kickboxing record and controversies of fighter ...\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportingnews.com/us/kickboxing/news/andrew-tate-mma-kickboxing-record-controversies/u50waalc9cfz7krjg9wnyb7p\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate kickboxing record After launching his career as a 20-year-old in 2007, Tate built a formidable kickboxing record that included 76 wins across 85 fights in more than 13 years in the ring.\\\\\", \\\\\"score\\\\\": 0.9976586, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.99701905, \\\\\"raw_content\\\\\": null}, {\\\\\"title\\\\\": \\\\\"Andrew Tate Bio, Wiki, Net Worth, Age, Family, MMA Career - Next Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nextbiography.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age. Andrew Tate is 36 years old as of 2023, born on December 1, 1986, in Washington, DC. By his mid-thirties, Andrew Tate has become an esteemed figure in the world of kickboxing, showcasing remarkable expertise and experience in the sport. Early Life of Andrew Tate. Andrew Tate was born on 01 December 1986 to an African-American\\\\\", \\\\\"score\\\\\": 0.99368566, \\\\\"raw_content\\\\\": null}]}\"}'\n",
-              "│   │   ],\n",
-              "│   │   'output': 'content: Andrew Tate\\'s kickboxing name is \"King Cobra.\" tool_calls: []'\n",
-              "}\n",
-              "]\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'content: Let me check the latest sports news. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='19bd3554-e670-4856-89d0-c63f5b016245', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m='bravy_search', \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"19bd3554-e670-4856-89d0-c63f5b016245\",\"tool_name\":\"bravy_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='526045a7-5f51-40fb-ba97-5ad29610e511', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'\u001b[0m\u001b[32m>\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"526045a7-5f51-40fb-ba97-5ad29610e511\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"526045a7-5f51-40fb-ba97-5ad29610e511\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.9996244, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.99909246, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Who is Andrew Tate? MMA, kickboxing record and controversies of fighter ...\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportingnews.com/us/kickboxing/news/andrew-tate-mma-kickboxing-record-controversies/u50waalc9cfz7krjg9wnyb7p\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate kickboxing record After launching his career as a 20-year-old in 2007, Tate built a formidable kickboxing record that included 76 wins across 85 fights in more than 13 years in the ring.\\\\\", \\\\\"score\\\\\": 0.9976586, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.99701905, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate Bio, Wiki, Net Worth, Age, Family, MMA Career - Next Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nextbiography.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age. Andrew Tate is 36 years old as of 2023, born on December 1, 1986, in Washington, DC. By his mid-thirties, Andrew Tate has become an esteemed figure in the world of kickboxing, showcasing remarkable expertise and experience in the sport. Early Life of Andrew Tate. Andrew Tate was born on 01 December 1986 to an African-American\\\\\", \\\\\"score\\\\\": 0.99368566, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input'\u001b[0m: \u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"system\",\"content\":\"You are a helpful assistant. Use search tool to answer the questions. \"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"Let me check the latest sports news.\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"19bd3554-e670-4856-89d0-c63f5b016245\",\"tool_name\":\"bravy_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Bill Cosby South Park episode\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"assistant\",\"content\":\"\",\"stop_reason\":\"end_of_turn\",\"tool_calls\":\u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"call_id\":\"526045a7-5f51-40fb-ba97-5ad29610e511\",\"tool_name\":\"brave_search\",\"arguments\":\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"query\":\"Andrew Tate kickboxing name\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"ipython\",\"call_id\":\"526045a7-5f51-40fb-ba97-5ad29610e511\",\"tool_name\":\"brave_search\",\"content\":\"\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"query\\\\\": \\\\\"Andrew Tate kickboxing name\\\\\", \\\\\"top_k\\\\\": \u001b[0m\u001b[32m[\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate kickboxing record: How many championships ... - FirstSportz\\\\\", \\\\\"url\\\\\": \\\\\"https://firstsportz.com/mma-how-many-championships-does-andrew-tate-have/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s Kickboxing career. During his kickboxing career, he used the nickname \\\\\\\\\\\\\"King Cobra,\\\\\\\\\\\\\" which he currently uses as his Twitter name. Tate had an unorthodox style of movement inside the ring. He kept his hands down most of the time and relied on quick jabs and an overhand right to land significant strikes.\\\\\", \\\\\"score\\\\\": 0.9996244, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate: Kickboxing Record, Facts, Height, Weight, Age, Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.lowkickmma.com/andrew-tate-kickboxing-record-facts-height-weight-age-biography/\\\\\", \\\\\"content\\\\\": \\\\\"Birth Name: Emory Andrew Tate III: Date of Birth: 1 December 1986: Place of Birth: Washington, D.C., U.S. ... In his professional kickboxing career, Andrew Tate won 32 of his fights by knockout.\\\\\", \\\\\"score\\\\\": 0.99909246, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Who is Andrew Tate? MMA, kickboxing record and controversies of fighter ...\\\\\", \\\\\"url\\\\\": \\\\\"https://www.sportingnews.com/us/kickboxing/news/andrew-tate-mma-kickboxing-record-controversies/u50waalc9cfz7krjg9wnyb7p\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate kickboxing record After launching his career as a 20-year-old in 2007, Tate built a formidable kickboxing record that included 76 wins across 85 fights in more than 13 years in the ring.\\\\\", \\\\\"score\\\\\": 0.9976586, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"About Andrew Tate: A Journey from Champion to Controversy\\\\\", \\\\\"url\\\\\": \\\\\"https://reachmorpheus.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate\\'s kickboxing career, beginning in 2005, is a tale of determination and skill. He quickly made a name for himself in the sport, rising through the ranks with his unique fighting style and strategic approach, honed by his chess-playing background.\\\\\", \\\\\"score\\\\\": 0.99701905, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m, \u001b[0m\u001b[32m{\u001b[0m\u001b[32m\\\\\"title\\\\\": \\\\\"Andrew Tate Bio, Wiki, Net Worth, Age, Family, MMA Career - Next Biography\\\\\", \\\\\"url\\\\\": \\\\\"https://www.nextbiography.com/andrew-tate/\\\\\", \\\\\"content\\\\\": \\\\\"Andrew Tate Age. Andrew Tate is 36 years old as of 2023, born on December 1, 1986, in Washington, DC. By his mid-thirties, Andrew Tate has become an esteemed figure in the world of kickboxing, showcasing remarkable expertise and experience in the sport. Early Life of Andrew Tate. Andrew Tate was born on 01 December 1986 to an African-American\\\\\", \\\\\"score\\\\\": 0.99368566, \\\\\"raw_content\\\\\": null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m]\u001b[0m\u001b[32m}\u001b[0m\u001b[32m\"\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m]\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'output'\u001b[0m: \u001b[32m'content: Andrew Tate\\'s kickboxing name is \"King Cobra.\" tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[1m]\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "print(f\"Getting traces for session_id={session_id}\")\n", - "import json\n", - "\n", - "from rich.pretty import pprint\n", - "\n", - "agent_logs = []\n", - "\n", - "for span in client.telemetry.query_spans(\n", - " attribute_filters=[\n", - " {\"key\": \"session_id\", \"op\": \"eq\", \"value\": session_id},\n", - " ],\n", - " attributes_to_return=[\"input\", \"output\"],\n", - "):\n", - " if span.attributes[\"output\"] != \"no shields\":\n", - " agent_logs.append(span.attributes)\n", - "\n", - "pprint(agent_logs)\n" - ] - }, - { - "cell_type": "markdown", - "id": "QF30H7ufP2RE", - "metadata": { - "id": "QF30H7ufP2RE" - }, - "source": [ - "##### 3.1.3 Post-Process Telemetry Results & Evaluate\n", - "\n", - "- Now, we want to run evaluation to assert that our search agent succesfully calls brave_search from online traces.\n", - "- We will first post-process the agent's telemetry logs and run evaluation." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "sy4Xaff_Avuu", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 411 - }, - "id": "sy4Xaff_Avuu", - "outputId": "cb68bae7-b21d-415d-8e71-612bd383c793" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
[\n",
-              "{\n",
-              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null}',\n",
-              "│   │   'generated_answer': 'content: Let me check the latest sports news. tool_calls: []',\n",
-              "│   │   'expected_answer': 'brave_search'\n",
-              "},\n",
-              "{\n",
-              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby (BSM-471) first appear? Give me the number and title.\",\"context\":null}',\n",
-              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='19bd3554-e670-4856-89d0-c63f5b016245', tool_name='bravy_search', arguments={'query': 'Bill Cosby South Park episode'})]\",\n",
-              "│   │   'expected_answer': 'brave_search'\n",
-              "},\n",
-              "{\n",
-              "│   │   'input_query': '{\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null}',\n",
-              "│   │   'generated_answer': \"content:  tool_calls: [ToolCall(call_id='526045a7-5f51-40fb-ba97-5ad29610e511', tool_name=<BuiltinTool.brave_search: 'brave_search'>, arguments={'query': 'Andrew Tate kickboxing name'})]\",\n",
-              "│   │   'expected_answer': 'brave_search'\n",
-              "}\n",
-              "]\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"Which teams played in the NBA western conference finals of 2024\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m'content: Let me check the latest sports news. tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32m]\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m: \u001b[32m'brave_search'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"In which episode and season of South Park does Bill Cosby \u001b[0m\u001b[32m(\u001b[0m\u001b[32mBSM-471\u001b[0m\u001b[32m)\u001b[0m\u001b[32m first appear? Give me the number and title.\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='19bd3554-e670-4856-89d0-c63f5b016245', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m='bravy_search', \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Bill Cosby South Park episode'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m: \u001b[32m'brave_search'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'input_query'\u001b[0m: \u001b[32m'\u001b[0m\u001b[32m{\u001b[0m\u001b[32m\"role\":\"user\",\"content\":\"What is the British-American kickboxer Andrew Tate\\'s kickboxing name?\",\"context\":null\u001b[0m\u001b[32m}\u001b[0m\u001b[32m'\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'generated_answer'\u001b[0m: \u001b[32m\"content: tool_calls: \u001b[0m\u001b[32m[\u001b[0m\u001b[32mToolCall\u001b[0m\u001b[32m(\u001b[0m\u001b[32mcall_id\u001b[0m\u001b[32m='526045a7-5f51-40fb-ba97-5ad29610e511', \u001b[0m\u001b[32mtool_name\u001b[0m\u001b[32m=\u001b[0m\u001b[32m<\u001b[0m\u001b[32mBuiltinTool.brave_search:\u001b[0m\u001b[32m 'brave_search'\u001b[0m\u001b[32m>\u001b[0m\u001b[32m, \u001b[0m\u001b[32marguments\u001b[0m\u001b[32m=\u001b[0m\u001b[32m{\u001b[0m\u001b[32m'query': 'Andrew Tate kickboxing name'\u001b[0m\u001b[32m}\u001b[0m\u001b[32m)\u001b[0m\u001b[32m]\u001b[0m\u001b[32m\"\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'expected_answer'\u001b[0m: \u001b[32m'brave_search'\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[1m]\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
ScoringScoreResponse(\n",
-              "results={\n",
-              "│   │   'basic::subset_of': ScoringResult(\n",
-              "│   │   │   aggregated_results={'accuracy': {'accuracy': 0.3333333333333333, 'num_correct': 1.0, 'num_total': 3}},\n",
-              "│   │   │   score_rows=[{'score': 0.0}, {'score': 0.0}, {'score': 1.0}]\n",
-              "│   │   )\n",
-              "}\n",
-              ")\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1;35mScoringScoreResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mresults\u001b[0m=\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'basic::subset_of'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m0.3333333333333333\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m3\u001b[0m\u001b[1m}\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m0.0\u001b[0m\u001b[1m}\u001b[0m, \u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "# post-process telemetry spance and prepare data for eval\n", - "# in this case, we want to assert that all user prompts is followed by a tool call\n", - "import ast\n", - "import json\n", - "\n", - "eval_rows = []\n", - "\n", - "for log in agent_logs:\n", - " last_msg = log[\"input\"][-1]\n", - " if '\"role\":\"user\"' in last_msg:\n", - " eval_rows.append(\n", - " {\n", - " \"input_query\": last_msg,\n", - " \"generated_answer\": log[\"output\"],\n", - " # check if generated_answer uses tools brave_search\n", - " \"expected_answer\": \"brave_search\",\n", - " },\n", - " )\n", - "\n", - "pprint(eval_rows)\n", - "scoring_params = {\n", - " \"basic::subset_of\": None,\n", - "}\n", - "scoring_response = client.scoring.score(\n", - " input_rows=eval_rows, scoring_functions=scoring_params\n", - ")\n", - "pprint(scoring_response)\n" - ] - }, - { - "cell_type": "markdown", - "id": "IKbzhxcw5e_c", - "metadata": { - "id": "IKbzhxcw5e_c" - }, - "source": [ - "#### 3.2. Agentic Application Dataset Scoring\n", - "- Llama Stack offers a library of scoring functions and the `/scoring` API, allowing you to run evaluations on your pre-annotated AI application datasets.\n", - "\n", - "- In this example, we will work with an example RAG dataset you have built previously, label with an annotation, and use LLM-As-Judge with custom judge prompt for scoring. Please checkout our [Llama Stack Playground](https://llama-stack.readthedocs.io/en/latest/playground/index.html) for an interactive interface to upload datasets and run scorings." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "xG4Y84VQBb0g", - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 298 - }, - "id": "xG4Y84VQBb0g", - "outputId": "f61cebdf-f614-440c-d170-f1e873b542ef" - }, - "outputs": [ - { - "data": { - "text/html": [ - "
ScoringScoreResponse(\n",
-              "results={\n",
-              "│   │   'llm-as-judge::base': ScoringResult(\n",
-              "│   │   │   aggregated_results={},\n",
-              "│   │   │   score_rows=[\n",
-              "│   │   │   │   {\n",
-              "│   │   │   │   │   'score': 'B',\n",
-              "│   │   │   │   │   'judge_feedback': 'Answer: B, Explanation: The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE and is fully consistent with it. The GENERATED_RESPONSE provides more detailed information about the top 5 topics related to LoRA, while the EXPECTED_RESPONSE only mentions \"LoRA\". The GENERATED_RESPONSE expands on the topic, but does not conflict with the EXPECTED_RESPONSE.'\n",
-              "│   │   │   │   }\n",
-              "│   │   │   ]\n",
-              "│   │   ),\n",
-              "│   │   'basic::subset_of': ScoringResult(\n",
-              "│   │   │   aggregated_results={'accuracy': 1.0, 'num_correct': 1.0, 'num_total': 1.0},\n",
-              "│   │   │   score_rows=[{'score': 1.0}]\n",
-              "│   │   )\n",
-              "}\n",
-              ")\n",
-              "
\n" - ], - "text/plain": [ - "\u001b[1;35mScoringScoreResponse\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[33mresults\u001b[0m=\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'llm-as-judge::base'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m{\u001b[0m\n", - "\u001b[2;32m│ │ │ │ │ \u001b[0m\u001b[32m'score'\u001b[0m: \u001b[32m'B'\u001b[0m,\n", - "\u001b[2;32m│ │ │ │ │ \u001b[0m\u001b[32m'judge_feedback'\u001b[0m: \u001b[32m'Answer: B, Explanation: The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE and is fully consistent with it. The GENERATED_RESPONSE provides more detailed information about the top 5 topics related to LoRA, while the EXPECTED_RESPONSE only mentions \"LoRA\". The GENERATED_RESPONSE expands on the topic, but does not conflict with the EXPECTED_RESPONSE.'\u001b[0m\n", - "\u001b[2;32m│ │ │ │ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[1m]\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m,\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[32m'basic::subset_of'\u001b[0m: \u001b[1;35mScoringResult\u001b[0m\u001b[1m(\u001b[0m\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33maggregated_results\u001b[0m=\u001b[1m{\u001b[0m\u001b[32m'accuracy'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_correct'\u001b[0m: \u001b[1;36m1.0\u001b[0m, \u001b[32m'num_total'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m,\n", - "\u001b[2;32m│ │ │ \u001b[0m\u001b[33mscore_rows\u001b[0m=\u001b[1m[\u001b[0m\u001b[1m{\u001b[0m\u001b[32m'score'\u001b[0m: \u001b[1;36m1.0\u001b[0m\u001b[1m}\u001b[0m\u001b[1m]\u001b[0m\n", - "\u001b[2;32m│ │ \u001b[0m\u001b[1m)\u001b[0m\n", - "\u001b[2;32m│ \u001b[0m\u001b[1m}\u001b[0m\n", - "\u001b[1m)\u001b[0m\n" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import rich\n", - "from rich.pretty import pprint\n", - "\n", - "judge_model_id = \"meta-llama/Llama-3.1-405B-Instruct-FP8\"\n", - "\n", - "JUDGE_PROMPT = \"\"\"\n", - "Given a QUESTION and GENERATED_RESPONSE and EXPECTED_RESPONSE.\n", - "\n", - "Compare the factual content of the GENERATED_RESPONSE with the EXPECTED_RESPONSE. Ignore any differences in style, grammar, or punctuation.\n", - " The GENERATED_RESPONSE may either be a subset or superset of the EXPECTED_RESPONSE, or it may conflict with it. Determine which case applies. Answer the question by selecting one of the following options:\n", - " (A) The GENERATED_RESPONSE is a subset of the EXPECTED_RESPONSE and is fully consistent with it.\n", - " (B) The GENERATED_RESPONSE is a superset of the EXPECTED_RESPONSE and is fully consistent with it.\n", - " (C) The GENERATED_RESPONSE contains all the same details as the EXPECTED_RESPONSE.\n", - " (D) There is a disagreement between the GENERATED_RESPONSE and the EXPECTED_RESPONSE.\n", - " (E) The answers differ, but these differences don't matter from the perspective of factuality.\n", - "\n", - "Give your answer in the format \"Answer: One of ABCDE, Explanation: \".\n", - "\n", - "Your actual task:\n", - "\n", - "QUESTION: {input_query}\n", - "GENERATED_RESPONSE: {generated_answer}\n", - "EXPECTED_RESPONSE: {expected_answer}\n", - "\"\"\"\n", - "\n", - "input_query = (\n", - " \"What are the top 5 topics that were explained? Only list succinct bullet points.\"\n", - ")\n", - "generated_answer = \"\"\"\n", - "Here are the top 5 topics that were explained in the documentation for Torchtune:\n", - "\n", - "* What is LoRA and how does it work?\n", - "* Fine-tuning with LoRA: memory savings and parameter-efficient finetuning\n", - "* Running a LoRA finetune with Torchtune: overview and recipe\n", - "* Experimenting with different LoRA configurations: rank, alpha, and attention modules\n", - "* LoRA finetuning\n", - "\"\"\"\n", - "expected_answer = \"\"\"LoRA\"\"\"\n", - "\n", - "rows = [\n", - " {\n", - " \"input_query\": input_query,\n", - " \"generated_answer\": generated_answer,\n", - " \"expected_answer\": expected_answer,\n", - " },\n", - "]\n", - "\n", - "scoring_params = {\n", - " \"llm-as-judge::base\": {\n", - " \"judge_model\": judge_model_id,\n", - " \"prompt_template\": JUDGE_PROMPT,\n", - " \"type\": \"llm_as_judge\",\n", - " \"judge_score_regexes\": [\"Answer: (A|B|C|D|E)\"],\n", - " },\n", - " \"basic::subset_of\": None,\n", - "}\n", - "\n", - "response = client.scoring.score(input_rows=rows, scoring_functions=scoring_params)\n", - "pprint(response)\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "rKtGo_v98UA2", - "metadata": { - "id": "rKtGo_v98UA2" - }, - "outputs": [], - "source": [] - } - ], - "metadata": { - "colab": { - "collapsed_sections": [ - "_JueJAKyJR5m" - ], - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "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.15" - }, - "widgets": { - "application/vnd.jupyter.widget-state+json": { - "0243626d7ef44ef2b90e8fed5c13183d": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "044d6d8dda1c4935b1752a9c71c6ee4a": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_63f34c3d43bb4fdd9faeb6161fd77285", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_5cb841b49eaa429e8616ec4b78f501e9", - "value": 1 - } - }, - "0640b57408644741970dd958ca0e21e6": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_6259ffc3ef674df985fd3fa4334f9c8e", - "IPY_MODEL_3d0376d2e574410eb4ef963d51cac0a6", - "IPY_MODEL_b66984cc5de541a5801a1e6e54d40daf" - ], - "layout": "IPY_MODEL_92135b9cb201475681ee0886887c84a8" - } - }, - "116139bfe7a44f969a2c97490c224d31": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_ab1f339cba094c918fc5507f8361de5c", - "placeholder": "​", - "style": "IPY_MODEL_a6a1eb412f204578b80e5b6717c1e3a5", - "value": " 1/1 [00:01<00:00,  1.27s/it]" - } - }, - "118b359b83304ae59fad57e28f621645": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "15d3ff07f1c54e58b51d452caca01209": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "17603dd7fedf4798a74533fbfd5bb421": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "186682be50c148c0826fa7c314087562": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_1f427d4273e04e19b1bdb13388736c01", - "placeholder": "​", - "style": "IPY_MODEL_38897429b7cf4077aea3a981593ca866", - "value": " 1/1 [00:00<00:00, 15.09it/s]" - } - }, - "1f427d4273e04e19b1bdb13388736c01": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2082554eed6644a996f0e31545789e08": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_a0be415018644c3cac098ab9b19c2391", - "IPY_MODEL_6ede3649e8c24015b3ca77490568bfcd", - "IPY_MODEL_116139bfe7a44f969a2c97490c224d31" - ], - "layout": "IPY_MODEL_243d13828d854880a6adb861ea867734" - } - }, - "2100363a158b4488a58620983aa5bdd4": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "243d13828d854880a6adb861ea867734": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "277101c35a784e6caf455a13cd9b8e59": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "2924814bab5748ddbeeedc70d324195e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_4738bccc6b384da5a20a8bcd61ecec59", - "IPY_MODEL_044d6d8dda1c4935b1752a9c71c6ee4a", - "IPY_MODEL_9277709ad9154d7b8f37d08db84ee425" - ], - "layout": "IPY_MODEL_f3f1f2487d6f455caeb6ec71a2d51ee2" - } - }, - "2958af7c9cdb46038e0336d6b7c6773e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "351928faa62543128e0bd29bf89bbf79": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "38897429b7cf4077aea3a981593ca866": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "3978f618c4f8467eb83c63a8f5aef98a": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "3d0376d2e574410eb4ef963d51cac0a6": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_9054d3825edb49cb9c35d24023f50c03", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_3978f618c4f8467eb83c63a8f5aef98a", - "value": 1 - } - }, - "425c6c0eaed741669551b9af77096c6f": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_d124b09896934d289df649375f455a8e", - "IPY_MODEL_554cff1a83d44bd2bbd36fd43acac7e2", - "IPY_MODEL_d0381718fc8b49a6ac7e7fe85cabba90" - ], - "layout": "IPY_MODEL_fd3daaf9093d45d8a9d39b87835f4582" - } - }, - "457374ae3035496eb943ad21484f76a0": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_bcf4679dda2d4767a0a24cbf236ca76e", - "IPY_MODEL_6e4ce98853c84beca11471e7ea9d97df", - "IPY_MODEL_186682be50c148c0826fa7c314087562" - ], - "layout": "IPY_MODEL_e1ef246e3e6c4359b7b61c341119e121" - } - }, - "45b569d733f944d29cefae8a5d13b215": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4738bccc6b384da5a20a8bcd61ecec59": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_66c92a8a89234a61a8c688cf1c3e29a1", - "placeholder": "​", - "style": "IPY_MODEL_ee1f4a0c85e44a3b849283337743a8d4", - "value": "Batches: 100%" - } - }, - "4a405d391b974e58a2c4fe00d4bb5815": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "4ad57f5d8a824afab639e8606ee43ca6": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "53865d3f918e468ab53504133b127973": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "554cff1a83d44bd2bbd36fd43acac7e2": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_6c60c8291e734f549e6c5a46b427b974", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_de88640505c24928904a3c76bda31c70", - "value": 1 - } - }, - "5afdb88e0159462e98773560e3dad439": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HBoxModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HBoxModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HBoxView", - "box_style": "", - "children": [ - "IPY_MODEL_f7bc4df675a141e380d965138552a142", - "IPY_MODEL_d7bf8b49145843ac98a6de424e628729", - "IPY_MODEL_8fb17faf68524de2b73321d71b80b407" - ], - "layout": "IPY_MODEL_45b569d733f944d29cefae8a5d13b215" - } - }, - "5cb841b49eaa429e8616ec4b78f501e9": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "5f19dab8c6da4050bc47fd78838f7530": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "6259ffc3ef674df985fd3fa4334f9c8e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_4a405d391b974e58a2c4fe00d4bb5815", - "placeholder": "​", - "style": "IPY_MODEL_2958af7c9cdb46038e0336d6b7c6773e", - "value": "Batches: 100%" - } - }, - "63f34c3d43bb4fdd9faeb6161fd77285": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "66c92a8a89234a61a8c688cf1c3e29a1": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "6c60c8291e734f549e6c5a46b427b974": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "6e4ce98853c84beca11471e7ea9d97df": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_a0ac7ee92d994c7b9b74e580ab2acdf7", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_118b359b83304ae59fad57e28f621645", - "value": 1 - } - }, - "6ede3649e8c24015b3ca77490568bfcd": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_f10237315e794539a00ca82bfff930be", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_ca09d2207b00456da4c37b5a782a190c", - "value": 1 - } - }, - "753dbe7891a143118b55eccf8c252e03": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "8fb17faf68524de2b73321d71b80b407": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_277101c35a784e6caf455a13cd9b8e59", - "placeholder": "​", - "style": "IPY_MODEL_d06666f765764f949e1876f2d5d67242", - "value": " 1/1 [00:01<00:00,  1.68s/it]" - } - }, - "9054d3825edb49cb9c35d24023f50c03": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "92135b9cb201475681ee0886887c84a8": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "9277709ad9154d7b8f37d08db84ee425": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_a447ea9af3e14e5e94eb14ed8dd3c0de", - "placeholder": "​", - "style": "IPY_MODEL_0243626d7ef44ef2b90e8fed5c13183d", - "value": " 1/1 [00:02<00:00,  2.65s/it]" - } - }, - "a0ac7ee92d994c7b9b74e580ab2acdf7": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a0be415018644c3cac098ab9b19c2391": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_e4b1dfe159304c5f88766b33e85a5c19", - "placeholder": "​", - "style": "IPY_MODEL_2100363a158b4488a58620983aa5bdd4", - "value": "Batches: 100%" - } - }, - "a447ea9af3e14e5e94eb14ed8dd3c0de": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "a6a1eb412f204578b80e5b6717c1e3a5": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "ab1f339cba094c918fc5507f8361de5c": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "b66984cc5de541a5801a1e6e54d40daf": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_efd68f6dc0b3428e8f5fc830c1bf2341", - "placeholder": "​", - "style": "IPY_MODEL_4ad57f5d8a824afab639e8606ee43ca6", - "value": " 1/1 [00:00<00:00,  5.36it/s]" - } - }, - "bbb93c771a9c453bb90e729b1f73b931": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "bcf4679dda2d4767a0a24cbf236ca76e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_bbb93c771a9c453bb90e729b1f73b931", - "placeholder": "​", - "style": "IPY_MODEL_351928faa62543128e0bd29bf89bbf79", - "value": "Batches: 100%" - } - }, - "ca09d2207b00456da4c37b5a782a190c": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "ce7de1af99434ad38a9382e7253dbfc0": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "d0381718fc8b49a6ac7e7fe85cabba90": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_fc086d0dd1a745308c59ae219ae135c5", - "placeholder": "​", - "style": "IPY_MODEL_15d3ff07f1c54e58b51d452caca01209", - "value": " 1/1 [00:00<00:00, 14.36it/s]" - } - }, - "d06666f765764f949e1876f2d5d67242": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "d124b09896934d289df649375f455a8e": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_753dbe7891a143118b55eccf8c252e03", - "placeholder": "​", - "style": "IPY_MODEL_ce7de1af99434ad38a9382e7253dbfc0", - "value": "Batches: 100%" - } - }, - "d7bf8b49145843ac98a6de424e628729": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "FloatProgressModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "FloatProgressModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "ProgressView", - "bar_style": "success", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_17603dd7fedf4798a74533fbfd5bb421", - "max": 1, - "min": 0, - "orientation": "horizontal", - "style": "IPY_MODEL_5f19dab8c6da4050bc47fd78838f7530", - "value": 1 - } - }, - "de88640505c24928904a3c76bda31c70": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "ProgressStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "ProgressStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "bar_color": null, - "description_width": "" - } - }, - "e1ef246e3e6c4359b7b61c341119e121": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "e4b1dfe159304c5f88766b33e85a5c19": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "ee1f4a0c85e44a3b849283337743a8d4": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "DescriptionStyleModel", - "state": { - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "DescriptionStyleModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "StyleView", - "description_width": "" - } - }, - "efd68f6dc0b3428e8f5fc830c1bf2341": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f10237315e794539a00ca82bfff930be": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f3f1f2487d6f455caeb6ec71a2d51ee2": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "f7bc4df675a141e380d965138552a142": { - "model_module": "@jupyter-widgets/controls", - "model_module_version": "1.5.0", - "model_name": "HTMLModel", - "state": { - "_dom_classes": [], - "_model_module": "@jupyter-widgets/controls", - "_model_module_version": "1.5.0", - "_model_name": "HTMLModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/controls", - "_view_module_version": "1.5.0", - "_view_name": "HTMLView", - "description": "", - "description_tooltip": null, - "layout": "IPY_MODEL_fdd057a4506f4f119d945bab5b930799", - "placeholder": "​", - "style": "IPY_MODEL_53865d3f918e468ab53504133b127973", - "value": "Batches: 100%" - } - }, - "fc086d0dd1a745308c59ae219ae135c5": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "fd3daaf9093d45d8a9d39b87835f4582": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - }, - "fdd057a4506f4f119d945bab5b930799": { - "model_module": "@jupyter-widgets/base", - "model_module_version": "1.2.0", - "model_name": "LayoutModel", - "state": { - "_model_module": "@jupyter-widgets/base", - "_model_module_version": "1.2.0", - "_model_name": "LayoutModel", - "_view_count": null, - "_view_module": "@jupyter-widgets/base", - "_view_module_version": "1.2.0", - "_view_name": "LayoutView", - "align_content": null, - "align_items": null, - "align_self": null, - "border": null, - "bottom": null, - "display": null, - "flex": null, - "flex_flow": null, - "grid_area": null, - "grid_auto_columns": null, - "grid_auto_flow": null, - "grid_auto_rows": null, - "grid_column": null, - "grid_gap": null, - "grid_row": null, - "grid_template_areas": null, - "grid_template_columns": null, - "grid_template_rows": null, - "height": null, - "justify_content": null, - "justify_items": null, - "left": null, - "margin": null, - "max_height": null, - "max_width": null, - "min_height": null, - "min_width": null, - "object_fit": null, - "object_position": null, - "order": null, - "overflow": null, - "overflow_x": null, - "overflow_y": null, - "padding": null, - "right": null, - "top": null, - "visibility": null, - "width": null - } - } - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 74e933cbfdf8a8a95a92a47488b4e509c6eaa410 Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Thu, 23 Jan 2025 11:39:33 -0800 Subject: [PATCH 104/107] More Updates to Read the Docs (#856) --- .../agent_execution_loop.md | 133 ++++++ .../building_applications/evaluation.md | 36 ++ docs/source/building_applications/index.md | 446 +----------------- docs/source/building_applications/rag.md | 92 ++++ docs/source/building_applications/safety.md | 21 + docs/source/distributions/building_distro.md | 362 ++++---------- docs/source/distributions/configuration.md | 13 +- .../distributions/importing_as_library.md | 32 +- 8 files changed, 405 insertions(+), 730 deletions(-) create mode 100644 docs/source/building_applications/agent_execution_loop.md create mode 100644 docs/source/building_applications/evaluation.md create mode 100644 docs/source/building_applications/rag.md create mode 100644 docs/source/building_applications/safety.md diff --git a/docs/source/building_applications/agent_execution_loop.md b/docs/source/building_applications/agent_execution_loop.md new file mode 100644 index 000000000..62fb314bc --- /dev/null +++ b/docs/source/building_applications/agent_execution_loop.md @@ -0,0 +1,133 @@ +# Agent Execution Loop + +Agents are the heart of complex AI applications. They combine inference, memory, safety, and tool usage into coherent workflows. At its core, an agent follows a sophisticated execution loop that enables multi-step reasoning, tool usage, and safety checks. + +Each agent turn follows these key steps: + +1. **Initial Safety Check**: The user's input is first screened through configured safety shields + +2. **Context Retrieval**: + - If RAG is enabled, the agent queries relevant documents from memory banks + - For new documents, they are first inserted into the memory bank + - Retrieved context is augmented to the user's prompt + +3. **Inference Loop**: The agent enters its main execution loop: + - The LLM receives the augmented prompt (with context and/or previous tool outputs) + - The LLM generates a response, potentially with tool calls + - If tool calls are present: + - Tool inputs are safety-checked + - Tools are executed (e.g., web search, code execution) + - Tool responses are fed back to the LLM for synthesis + - The loop continues until: + - The LLM provides a final response without tool calls + - Maximum iterations are reached + - Token limit is exceeded + +4. **Final Safety Check**: The agent's final response is screened through safety shields + +```{mermaid} +sequenceDiagram + participant U as User + participant E as Executor + participant M as Memory Bank + participant L as LLM + participant T as Tools + participant S as Safety Shield + + Note over U,S: Agent Turn Start + U->>S: 1. Submit Prompt + activate S + S->>E: Input Safety Check + deactivate S + + E->>M: 2.1 Query Context + M-->>E: 2.2 Retrieved Documents + + loop Inference Loop + E->>L: 3.1 Augment with Context + L-->>E: 3.2 Response (with/without tool calls) + + alt Has Tool Calls + E->>S: Check Tool Input + S->>T: 4.1 Execute Tool + T-->>E: 4.2 Tool Response + E->>L: 5.1 Tool Response + L-->>E: 5.2 Synthesized Response + end + + opt Stop Conditions + Note over E: Break if: + Note over E: - No tool calls + Note over E: - Max iterations reached + Note over E: - Token limit exceeded + end + end + + E->>S: Output Safety Check + S->>U: 6. Final Response +``` + +Each step in this process can be monitored and controlled through configurations. Here's an example that demonstrates monitoring the agent's execution: + +```python +from llama_stack_client.lib.agents.event_logger import EventLogger + +agent_config = AgentConfig( + model="Llama3.2-3B-Instruct", + instructions="You are a helpful assistant", + # Enable both RAG and tool usage + tools=[ + { + "type": "memory", + "memory_bank_configs": [{ + "type": "vector", + "bank_id": "my_docs" + }], + "max_tokens_in_context": 4096 + }, + { + "type": "code_interpreter", + "enable_inline_code_execution": True + } + ], + # Configure safety + input_shields=["content_safety"], + output_shields=["content_safety"], + # Control the inference loop + max_infer_iters=5, + sampling_params={ + "strategy": { + "type": "top_p", + "temperature": 0.7, + "top_p": 0.95 + }, + "max_tokens": 2048 + } +) + +agent = Agent(client, agent_config) +session_id = agent.create_session("monitored_session") + +# Stream the agent's execution steps +response = agent.create_turn( + messages=[{"role": "user", "content": "Analyze this code and run it"}], + attachments=[{ + "content": "https://raw.githubusercontent.com/example/code.py", + "mime_type": "text/plain" + }], + session_id=session_id +) + +# Monitor each step of execution +for log in EventLogger().log(response): + if log.event.step_type == "memory_retrieval": + print("Retrieved context:", log.event.retrieved_context) + elif log.event.step_type == "inference": + print("LLM output:", log.event.model_response) + elif log.event.step_type == "tool_execution": + print("Tool call:", log.event.tool_call) + print("Tool response:", log.event.tool_response) + elif log.event.step_type == "shield_call": + if log.event.violation: + print("Safety violation:", log.event.violation) +``` diff --git a/docs/source/building_applications/evaluation.md b/docs/source/building_applications/evaluation.md new file mode 100644 index 000000000..473deaee2 --- /dev/null +++ b/docs/source/building_applications/evaluation.md @@ -0,0 +1,36 @@ +## Testing & Evaluation + +Llama Stack provides built-in tools for evaluating your applications: + +1. **Benchmarking**: Test against standard datasets +2. **Application Evaluation**: Score your application's outputs +3. **Custom Metrics**: Define your own evaluation criteria + +Here's how to set up basic evaluation: + +```python +# Create an evaluation task +response = client.eval_tasks.register( + eval_task_id="my_eval", + dataset_id="my_dataset", + scoring_functions=["accuracy", "relevance"] +) + +# Run evaluation +job = client.eval.run_eval( + task_id="my_eval", + task_config={ + "type": "app", + "eval_candidate": { + "type": "agent", + "config": agent_config + } + } +) + +# Get results +result = client.eval.job_result( + task_id="my_eval", + job_id=job.job_id +) +``` diff --git a/docs/source/building_applications/index.md b/docs/source/building_applications/index.md index b9170e092..6e1e9454f 100644 --- a/docs/source/building_applications/index.md +++ b/docs/source/building_applications/index.md @@ -1,446 +1,26 @@ # Building AI Applications -[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1F2ksmkoGQPa4pzRjMOE6BXWeOxWFIW6n?usp=sharing) +Llama Stack provides all the building blocks needed to create sophisticated AI applications. -Llama Stack provides all the building blocks needed to create sophisticated AI applications. This guide will walk you through how to use these components effectively. Check out our Colab notebook on to follow along working examples on how you can build LLM-powered agentic applications using Llama Stack. +The best way to get started is to look at this notebook which walks through the various APIs (from basic inference, to RAG agents) and how to use them. -## Basic Inference +**Notebook**: [Building AI Applications](docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb) -The foundation of any AI application is the ability to interact with LLM models. Llama Stack provides a simple interface for both completion and chat-based inference: +## Agentic Concepts +- **[Agent Execution Loop](agent_execution_loop)** +- **[RAG](rag)** +- **[Safety](safety)** +- **[Tools](tools)** +- **[Telemetry](telemetry)** -```python -from llama_stack_client import LlamaStackClient -client = LlamaStackClient(base_url="http://localhost:5001") - -# List available models -models = client.models.list() - -# Simple chat completion -response = client.inference.chat_completion( - model_id="Llama3.2-3B-Instruct", - messages=[ - {"role": "system", "content": "You are a helpful assistant."}, - {"role": "user", "content": "Write a haiku about coding"} - ] -) -print(response.completion_message.content) -``` - -## Adding Memory & RAG - -Memory enables your applications to reference and recall information from previous interactions or external documents. Llama Stack's memory system is built around the concept of Memory Banks: - -1. **Vector Memory Banks**: For semantic search and retrieval -2. **Key-Value Memory Banks**: For structured data storage -3. **Keyword Memory Banks**: For basic text search -4. **Graph Memory Banks**: For relationship-based retrieval - -Here's how to set up a vector memory bank for RAG: - -```python -# Register a memory bank -bank_id = "my_documents" -response = client.memory_banks.register( - memory_bank_id=bank_id, - params={ - "memory_bank_type": "vector", - "embedding_model": "all-MiniLM-L6-v2", - "chunk_size_in_tokens": 512 - } -) - -# Insert documents -documents = [ - { - "document_id": "doc1", - "content": "Your document text here", - "mime_type": "text/plain" - } -] -client.memory.insert(bank_id, documents) - -# Query documents -results = client.memory.query( - bank_id=bank_id, - query="What do you know about...", -) -``` - -## Implementing Safety Guardrails - -Safety is a critical component of any AI application. Llama Stack provides a Shield system that can be applied at multiple touchpoints: - -```python -# Register a safety shield -shield_id = "content_safety" -client.shields.register( - shield_id=shield_id, - provider_shield_id="llama-guard-basic" -) - -# Run content through shield -response = client.safety.run_shield( - shield_id=shield_id, - messages=[{"role": "user", "content": "User message here"}] -) - -if response.violation: - print(f"Safety violation detected: {response.violation.user_message}") -``` - -## Building Agents - -Agents are the heart of complex AI applications. They combine inference, memory, safety, and tool usage into coherent workflows. At its core, an agent follows a sophisticated execution loop that enables multi-step reasoning, tool usage, and safety checks. - -### The Agent Execution Loop - -Each agent turn follows these key steps: - -1. **Initial Safety Check**: The user's input is first screened through configured safety shields - -2. **Context Retrieval**: - - If RAG is enabled, the agent queries relevant documents from memory banks - - For new documents, they are first inserted into the memory bank - - Retrieved context is augmented to the user's prompt - -3. **Inference Loop**: The agent enters its main execution loop: - - The LLM receives the augmented prompt (with context and/or previous tool outputs) - - The LLM generates a response, potentially with tool calls - - If tool calls are present: - - Tool inputs are safety-checked - - Tools are executed (e.g., web search, code execution) - - Tool responses are fed back to the LLM for synthesis - - The loop continues until: - - The LLM provides a final response without tool calls - - Maximum iterations are reached - - Token limit is exceeded - -4. **Final Safety Check**: The agent's final response is screened through safety shields - -```{mermaid} -sequenceDiagram - participant U as User - participant E as Executor - participant M as Memory Bank - participant L as LLM - participant T as Tools - participant S as Safety Shield - - Note over U,S: Agent Turn Start - U->>S: 1. Submit Prompt - activate S - S->>E: Input Safety Check - deactivate S - - E->>M: 2.1 Query Context - M-->>E: 2.2 Retrieved Documents - - loop Inference Loop - E->>L: 3.1 Augment with Context - L-->>E: 3.2 Response (with/without tool calls) - - alt Has Tool Calls - E->>S: Check Tool Input - S->>T: 4.1 Execute Tool - T-->>E: 4.2 Tool Response - E->>L: 5.1 Tool Response - L-->>E: 5.2 Synthesized Response - end - - opt Stop Conditions - Note over E: Break if: - Note over E: - No tool calls - Note over E: - Max iterations reached - Note over E: - Token limit exceeded - end - end - - E->>S: Output Safety Check - S->>U: 6. Final Response -``` - -Each step in this process can be monitored and controlled through configurations. Here's an example that demonstrates monitoring the agent's execution: - -```python -from llama_stack_client.lib.agents.event_logger import EventLogger - -agent_config = AgentConfig( - model="Llama3.2-3B-Instruct", - instructions="You are a helpful assistant", - # Enable both RAG and tool usage - tools=[ - { - "type": "memory", - "memory_bank_configs": [{ - "type": "vector", - "bank_id": "my_docs" - }], - "max_tokens_in_context": 4096 - }, - { - "type": "code_interpreter", - "enable_inline_code_execution": True - } - ], - # Configure safety - input_shields=["content_safety"], - output_shields=["content_safety"], - # Control the inference loop - max_infer_iters=5, - sampling_params={ - "strategy": { - "type": "top_p", - "temperature": 0.7, - "top_p": 0.95 - }, - "max_tokens": 2048 - } -) - -agent = Agent(client, agent_config) -session_id = agent.create_session("monitored_session") - -# Stream the agent's execution steps -response = agent.create_turn( - messages=[{"role": "user", "content": "Analyze this code and run it"}], - attachments=[{ - "content": "https://raw.githubusercontent.com/example/code.py", - "mime_type": "text/plain" - }], - session_id=session_id -) - -# Monitor each step of execution -for log in EventLogger().log(response): - if log.event.step_type == "memory_retrieval": - print("Retrieved context:", log.event.retrieved_context) - elif log.event.step_type == "inference": - print("LLM output:", log.event.model_response) - elif log.event.step_type == "tool_execution": - print("Tool call:", log.event.tool_call) - print("Tool response:", log.event.tool_response) - elif log.event.step_type == "shield_call": - if log.event.violation: - print("Safety violation:", log.event.violation) -``` - -This example shows how an agent can: Llama Stack provides a high-level agent framework: - -```python -from llama_stack_client.lib.agents.agent import Agent -from llama_stack_client.types.agent_create_params import AgentConfig - -# Configure an agent -agent_config = AgentConfig( - model="Llama3.2-3B-Instruct", - instructions="You are a helpful assistant", - tools=[ - { - "type": "memory", - "memory_bank_configs": [], - "query_generator_config": { - "type": "default", - "sep": " " - } - } - ], - input_shields=["content_safety"], - output_shields=["content_safety"], - enable_session_persistence=True -) - -# Create an agent -agent = Agent(client, agent_config) -session_id = agent.create_session("my_session") - -# Run agent turns -response = agent.create_turn( - messages=[{"role": "user", "content": "Your question here"}], - session_id=session_id -) -``` - -### Adding Tools to Agents ```{toctree} :hidden: -:maxdepth: 3 +:maxdepth: 1 +agent_execution_loop +rag +safety tools -``` - -Agents can be enhanced with various tools. For detailed information about available tools, their configuration, and providers, see the [Tools](tools.md) documentation. - -Tools are configured through the `toolgroups` parameter in the agent configuration. Each tool group can be specified either as a string or with additional arguments: - -```python -from llama_stack_client.lib.agents.agent import Agent -from llama_stack_client.types.agent_create_params import AgentConfig - -agent_config = AgentConfig( - model="Llama3.2-3B-Instruct", - instructions="You are a helpful assistant", - # Configure tool groups - toolgroups=[ - # Simple string format - "builtin::code_interpreter", - # With arguments format - { - "name": "builtin::websearch", - "args": { - "max_results": 5 - } - } - ], - tool_choice="auto", - tool_prompt_format="json", - # Optional safety configuration - input_shields=["content_safety"], - output_shields=["content_safety"], - # Control the inference loop - max_infer_iters=10, - sampling_params={ - "strategy": { - "type": "top_p", - "temperature": 0.7, - "top_p": 0.95 - }, - "max_tokens": 2048 - } -) - -agent = Agent(client, agent_config) -``` - -For details on available tool groups, providers, and their configuration options, refer to the [Tools](tools.md) documentation. - -## Building RAG-Enhanced Agents - -One of the most powerful patterns is combining agents with RAG capabilities. Here's a complete example: - -```python -from llama_stack_client.types import Attachment - -# Create attachments from documents -attachments = [ - Attachment( - content="https://raw.githubusercontent.com/example/doc.rst", - mime_type="text/plain" - ) -] - -# Configure agent with memory -agent_config = AgentConfig( - model="Llama3.2-3B-Instruct", - instructions="You are a helpful assistant", - tools=[{ - "type": "memory", - "memory_bank_configs": [], - "query_generator_config": {"type": "default", "sep": " "}, - "max_tokens_in_context": 4096, - "max_chunks": 10 - }], - enable_session_persistence=True -) - -agent = Agent(client, agent_config) -session_id = agent.create_session("rag_session") - -# Initial document ingestion -response = agent.create_turn( - messages=[{ - "role": "user", - "content": "I am providing some documents for reference." - }], - attachments=attachments, - session_id=session_id -) - -# Query with RAG -response = agent.create_turn( - messages=[{ - "role": "user", - "content": "What are the key topics in the documents?" - }], - session_id=session_id -) -``` - -## Testing & Evaluation - -Llama Stack provides built-in tools for evaluating your applications: - -1. **Benchmarking**: Test against standard datasets -2. **Application Evaluation**: Score your application's outputs -3. **Custom Metrics**: Define your own evaluation criteria - -Here's how to set up basic evaluation: - -```python -# Create an evaluation task -response = client.eval_tasks.register( - eval_task_id="my_eval", - dataset_id="my_dataset", - scoring_functions=["accuracy", "relevance"] -) - -# Run evaluation -job = client.eval.run_eval( - task_id="my_eval", - task_config={ - "type": "app", - "eval_candidate": { - "type": "agent", - "config": agent_config - } - } -) - -# Get results -result = client.eval.job_result( - task_id="my_eval", - job_id=job.job_id -) -``` - -## Debugging & Monitoring - -Llama Stack includes comprehensive telemetry for debugging and monitoring your applications: - -1. **Tracing**: Track request flows across components -2. **Metrics**: Measure performance and usage -3. **Logging**: Debug issues and track behavior - -The telemetry system supports multiple output formats: - -- OpenTelemetry for visualization in tools like Jaeger -- SQLite for local storage and querying -- Console output for development - -Example of querying traces: - -```python -# Query traces for a session -traces = client.telemetry.query_traces( - attribute_filters=[{ - "key": "session_id", - "op": "eq", - "value": session_id - }] -) - -# Get spans within the root span; indexed by ID -# Use parent_span_id to build a tree out of it -spans_by_id = client.telemetry.get_span_tree( - span_id=traces[0].root_span_id -) -``` - -For details on how to use the telemetry system to debug your applications, export traces to a dataset, and run evaluations, see the [Telemetry](telemetry) section. - -```{toctree} -:hidden: -:maxdepth: 3 - telemetry ``` diff --git a/docs/source/building_applications/rag.md b/docs/source/building_applications/rag.md new file mode 100644 index 000000000..17ecd2046 --- /dev/null +++ b/docs/source/building_applications/rag.md @@ -0,0 +1,92 @@ +## Memory & RAG + +Memory enables your applications to reference and recall information from previous interactions or external documents. Llama Stack's memory system is built around the concept of Memory Banks: + +1. **Vector Memory Banks**: For semantic search and retrieval +2. **Key-Value Memory Banks**: For structured data storage +3. **Keyword Memory Banks**: For basic text search +4. **Graph Memory Banks**: For relationship-based retrieval + +Here's how to set up a vector memory bank for RAG: + +```python +# Register a memory bank +bank_id = "my_documents" +response = client.memory_banks.register( + memory_bank_id=bank_id, + params={ + "memory_bank_type": "vector", + "embedding_model": "all-MiniLM-L6-v2", + "chunk_size_in_tokens": 512 + } +) + +# Insert documents +documents = [ + { + "document_id": "doc1", + "content": "Your document text here", + "mime_type": "text/plain" + } +] +client.memory.insert(bank_id, documents) + +# Query documents +results = client.memory.query( + bank_id=bank_id, + query="What do you know about...", +) +``` + + +### Building RAG-Enhanced Agents + +One of the most powerful patterns is combining agents with RAG capabilities. Here's a complete example: + +```python +from llama_stack_client.types import Attachment + +# Create attachments from documents +attachments = [ + Attachment( + content="https://raw.githubusercontent.com/example/doc.rst", + mime_type="text/plain" + ) +] + +# Configure agent with memory +agent_config = AgentConfig( + model="Llama3.2-3B-Instruct", + instructions="You are a helpful assistant", + tools=[{ + "type": "memory", + "memory_bank_configs": [], + "query_generator_config": {"type": "default", "sep": " "}, + "max_tokens_in_context": 4096, + "max_chunks": 10 + }], + enable_session_persistence=True +) + +agent = Agent(client, agent_config) +session_id = agent.create_session("rag_session") + +# Initial document ingestion +response = agent.create_turn( + messages=[{ + "role": "user", + "content": "I am providing some documents for reference." + }], + attachments=attachments, + session_id=session_id +) + +# Query with RAG +response = agent.create_turn( + messages=[{ + "role": "user", + "content": "What are the key topics in the documents?" + }], + session_id=session_id +) +``` diff --git a/docs/source/building_applications/safety.md b/docs/source/building_applications/safety.md new file mode 100644 index 000000000..31efa0f8c --- /dev/null +++ b/docs/source/building_applications/safety.md @@ -0,0 +1,21 @@ +## Safety Guardrails + +Safety is a critical component of any AI application. Llama Stack provides a Shield system that can be applied at multiple touchpoints: + +```python +# Register a safety shield +shield_id = "content_safety" +client.shields.register( + shield_id=shield_id, + provider_shield_id="llama-guard-basic" +) + +# Run content through shield +response = client.safety.run_shield( + shield_id=shield_id, + messages=[{"role": "user", "content": "User message here"}] +) + +if response.violation: + print(f"Safety violation detected: {response.violation.user_message}") +``` diff --git a/docs/source/distributions/building_distro.md b/docs/source/distributions/building_distro.md index 9034a1811..5556d4aa1 100644 --- a/docs/source/distributions/building_distro.md +++ b/docs/source/distributions/building_distro.md @@ -13,24 +13,94 @@ In order to build your own distribution, we recommend you clone the `llama-stack git clone git@github.com:meta-llama/llama-stack.git cd llama-stack pip install -e . - -llama stack build -h ``` +Use the CLI to build your distribution. +The main points to consider are: +1. **Image Type** - Do you want a Conda / venv environment or a Container (eg. Docker) +2. **Template** - Do you want to use a template to build your distribution? or start from scratch ? +3. **Config** - Do you want to use a pre-existing config file to build your distribution? -We will start build our distribution (in the form of a Conda environment, or Container image). In this step, we will specify: -- `name`: the name for our distribution (e.g. `my-stack`) -- `image_type`: our build image type (`conda | container`) -- `distribution_spec`: our distribution specs for specifying API providers - - `description`: a short description of the configurations for the distribution - - `providers`: specifies the underlying implementation for serving each API endpoint - - `image_type`: `conda` | `container` to specify whether to build the distribution in the form of Container image or Conda environment. +``` +llama stack build -h + +usage: llama stack build [-h] [--config CONFIG] [--template TEMPLATE] [--list-templates | --no-list-templates] [--image-type {conda,container,venv}] [--image-name IMAGE_NAME] + +Build a Llama stack container + +options: + -h, --help show this help message and exit + --config CONFIG Path to a config file to use for the build. You can find example configs in llama_stack/distribution/**/build.yaml. + If this argument is not provided, you will be prompted to enter information interactively + --template TEMPLATE Name of the example template config to use for build. You may use `llama stack build --list-templates` to check out the available templates + --list-templates, --no-list-templates + Show the available templates for building a Llama Stack distribution (default: False) + --image-type {conda,container,venv} + Image Type to use for the build. This can be either conda or container or venv. If not specified, will use the image type from the template config. + --image-name IMAGE_NAME + [for image-type=conda] Name of the conda environment to use for the build. If + not specified, currently active Conda environment will be used. If no Conda + environment is active, you must specify a name. +``` After this step is complete, a file named `-build.yaml` and template file `-run.yaml` will be generated and saved at the output file path specified at the end of the command. ::::{tab-set} +:::{tab-item} Building from a template +To build from alternative API providers, we provide distribution templates for users to get started building a distribution backed by different providers. + +The following command will allow you to see the available templates and their corresponding providers. +``` +llama stack build --list-templates +``` + +``` +------------------------------+-----------------------------------------------------------------------------+ +| Template Name | Description | ++------------------------------+-----------------------------------------------------------------------------+ +| hf-serverless | Use (an external) Hugging Face Inference Endpoint for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| together | Use Together.AI for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| vllm-gpu | Use a built-in vLLM engine for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| experimental-post-training | Experimental template for post training | ++------------------------------+-----------------------------------------------------------------------------+ +| remote-vllm | Use (an external) vLLM server for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| fireworks | Use Fireworks.AI for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| tgi | Use (an external) TGI server for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| bedrock | Use AWS Bedrock for running LLM inference and safety | ++------------------------------+-----------------------------------------------------------------------------+ +| meta-reference-gpu | Use Meta Reference for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| nvidia | Use NVIDIA NIM for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| meta-reference-quantized-gpu | Use Meta Reference with fp8, int4 quantization for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| cerebras | Use Cerebras for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| ollama | Use (an external) Ollama server for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +| hf-endpoint | Use (an external) Hugging Face Inference Endpoint for running LLM inference | ++------------------------------+-----------------------------------------------------------------------------+ +``` + +You may then pick a template to build your distribution with providers fitted to your liking. + +For example, to build a distribution with TGI as the inference provider, you can run: +``` +$ llama stack build --template tgi +... +You can now edit ~/.llama/distributions/llamastack-tgi/tgi-run.yaml and run `llama stack run ~/.llama/distributions/llamastack-tgi/tgi-run.yaml` +``` +::: :::{tab-item} Building from Scratch -- For a new user, we could start off with running `llama stack build` which will allow you to a interactively enter wizard where you will be prompted to enter build configurations. +If the provided templates do not fit your use case, you could start off with running `llama stack build` which will allow you to a interactively enter wizard where you will be prompted to enter build configurations. + +It would be best to start with a template and understand the structure of the config file and the various concepts ( APIS, providers, resources, etc.) before starting from scratch. ``` llama stack build @@ -57,272 +127,6 @@ You can now edit ~/.llama/distributions/llamastack-my-local-stack/my-local-stack ``` ::: -:::{tab-item} Building from a template -- To build from alternative API providers, we provide distribution templates for users to get started building a distribution backed by different providers. - -The following command will allow you to see the available templates and their corresponding providers. -``` -llama stack build --list-templates -``` - -``` -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| Template Name | Providers | Description | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| tgi | { | Use (an external) TGI server for running LLM inference | -| | "inference": [ | | -| | "remote::tgi" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| remote-vllm | { | Use (an external) vLLM server for running LLM inference | -| | "inference": [ | | -| | "remote::vllm" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| vllm-gpu | { | Use a built-in vLLM engine for running LLM inference | -| | "inference": [ | | -| | "inline::vllm" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| meta-reference-quantized-gpu | { | Use Meta Reference with fp8, int4 quantization for running LLM inference | -| | "inference": [ | | -| | "inline::meta-reference-quantized" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| meta-reference-gpu | { | Use Meta Reference for running LLM inference | -| | "inference": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| hf-serverless | { | Use (an external) Hugging Face Inference Endpoint for running LLM inference | -| | "inference": [ | | -| | "remote::hf::serverless" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| together | { | Use Together.AI for running LLM inference | -| | "inference": [ | | -| | "remote::together" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| ollama | { | Use (an external) Ollama server for running LLM inference | -| | "inference": [ | | -| | "remote::ollama" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| bedrock | { | Use AWS Bedrock for running LLM inference and safety | -| | "inference": [ | | -| | "remote::bedrock" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "remote::bedrock" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| hf-endpoint | { | Use (an external) Hugging Face Inference Endpoint for running LLM inference | -| | "inference": [ | | -| | "remote::hf::endpoint" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| fireworks | { | Use Fireworks.AI for running LLM inference | -| | "inference": [ | | -| | "remote::fireworks" | | -| | ], | | -| | "memory": [ | | -| | "inline::faiss", | | -| | "remote::chromadb", | | -| | "remote::pgvector" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -| cerebras | { | Use Cerebras for running LLM inference | -| | "inference": [ | | -| | "remote::cerebras" | | -| | ], | | -| | "safety": [ | | -| | "inline::llama-guard" | | -| | ], | | -| | "memory": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "agents": [ | | -| | "inline::meta-reference" | | -| | ], | | -| | "telemetry": [ | | -| | "inline::meta-reference" | | -| | ] | | -| | } | | -+------------------------------+----------------------------------------+-----------------------------------------------------------------------------+ -``` - -You may then pick a template to build your distribution with providers fitted to your liking. - -For example, to build a distribution with TGI as the inference provider, you can run: -``` -llama stack build --template tgi -``` - -``` -$ llama stack build --template tgi -... -You can now edit ~/.llama/distributions/llamastack-tgi/tgi-run.yaml and run `llama stack run ~/.llama/distributions/llamastack-tgi/tgi-run.yaml` -``` -::: - :::{tab-item} Building from a pre-existing build config file - In addition to templates, you may customize the build to your liking through editing config files and build from config files with the following command. @@ -377,6 +181,10 @@ After this step is successful, you should be able to find the built container im Now, let's start the Llama Stack Distribution Server. You will need the YAML configuration file which was written out at the end by the `llama stack build` step. ``` +# Start using template name +llama stack run tgi + +# Start using config file llama stack run ~/.llama/distributions/llamastack-my-local-stack/my-local-stack-run.yaml ``` @@ -412,4 +220,4 @@ INFO: 2401:db00:35c:2d2b:face:0:c9:0:54678 - "GET /models/list HTTP/1.1" 200 ### Troubleshooting -If you encounter any issues, search through our [GitHub Issues](https://github.com/meta-llama/llama-stack/issues), or file an new issue. +If you encounter any issues, ask questions in our discord or search through our [GitHub Issues](https://github.com/meta-llama/llama-stack/issues), or file an new issue. diff --git a/docs/source/distributions/configuration.md b/docs/source/distributions/configuration.md index 41df26618..d12f584f7 100644 --- a/docs/source/distributions/configuration.md +++ b/docs/source/distributions/configuration.md @@ -70,20 +70,27 @@ Next up is the most critical part: the set of providers that the stack will use ```yaml providers: inference: + # provider_id is a string you can choose freely - provider_id: ollama + # provider_type is a string that specifies the type of provider. + # in this case, the provider for inference is ollama and it is run remotely (outside of the distribution) provider_type: remote::ollama + # config is a dictionary that contains the configuration for the provider. + # in this case, the configuration is the url of the ollama server config: url: ${env.OLLAMA_URL:http://localhost:11434} ``` A few things to note: -- A _provider instance_ is identified with an (identifier, type, configuration) tuple. The identifier is a string you can choose freely. +- A _provider instance_ is identified with an (id, type, configuration) triplet. +- The id is a string you can choose freely. - You can instantiate any number of provider instances of the same type. -- The configuration dictionary is provider-specific. Notice that configuration can reference environment variables (with default values), which are expanded at runtime. When you run a stack server (via docker or via `llama stack run`), you can specify `--env OLLAMA_URL=http://my-server:11434` to override the default value. +- The configuration dictionary is provider-specific. +- Notice that configuration can reference environment variables (with default values), which are expanded at runtime. When you run a stack server (via docker or via `llama stack run`), you can specify `--env OLLAMA_URL=http://my-server:11434` to override the default value. ## Resources -``` Finally, let's look at the `models` section: + ```yaml models: - metadata: {} diff --git a/docs/source/distributions/importing_as_library.md b/docs/source/distributions/importing_as_library.md index 7e15062df..cc7ed1beb 100644 --- a/docs/source/distributions/importing_as_library.md +++ b/docs/source/distributions/importing_as_library.md @@ -1,11 +1,20 @@ # Using Llama Stack as a Library -If you are planning to use an external service for Inference (even Ollama or TGI counts as external), it is often easier to use Llama Stack as a library. This avoids the overhead of setting up a server. For [example](https://github.com/meta-llama/llama-stack-client-python/blob/main/src/llama_stack_client/lib/direct/test.py): +If you are planning to use an external service for Inference (even Ollama or TGI counts as external), it is often easier to use Llama Stack as a library. This avoids the overhead of setting up a server. +```python +# setup +pip install llama-stack +llama stack build --template together --image-type venv +``` ```python -from llama_stack_client.lib.direct.direct import LlamaStackDirectClient +from llama_stack.distribution.library_client import LlamaStackAsLibraryClient -client = await LlamaStackDirectClient.from_template('ollama') +client = LlamaStackAsLibraryClient( + "ollama", + # provider_data is optional, but if you need to pass in any provider specific data, you can do so here. + provider_data = {"tavily_search_api_key": os.environ['TAVILY_SEARCH_API_KEY']} +) await client.initialize() ``` @@ -14,23 +23,12 @@ This will parse your config and set up any inline implementations and remote cli Then, you can access the APIs like `models` and `inference` on the client and call their methods directly: ```python -response = await client.models.list() -print(response) -``` - -```python -response = await client.inference.chat_completion( - messages=[UserMessage(content="What is the capital of France?", role="user")], - model_id="Llama3.1-8B-Instruct", - stream=False, -) -print("\nChat completion response:") -print(response) +response = client.models.list() ``` If you've created a [custom distribution](https://llama-stack.readthedocs.io/en/latest/distributions/building_distro.html), you can also use the run.yaml configuration file directly: ```python -client = await LlamaStackDirectClient.from_config(config_path) -await client.initialize() +client = LlamaStackAsLibraryClient(config_path) +client.initialize() ``` From a10cdc7cdb6142fe66923b2a511e9c7744e5ee4a Mon Sep 17 00:00:00 2001 From: Hardik Shah Date: Thu, 23 Jan 2025 12:00:01 -0800 Subject: [PATCH 105/107] Update README.md --- README.md | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b1878d7e4..9d6009bae 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,13 @@ [**Quick Start**](https://llama-stack.readthedocs.io/en/latest/getting_started/index.html) | [**Documentation**](https://llama-stack.readthedocs.io/en/latest/index.html) | [**Colab Notebook**](./docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb) -Llama Stack defines and standardizes the core building blocks needed to bring generative AI applications to market. It provides a unified set of APIs with implementations from leading service providers, enabling seamless transitions between development and production environments. +Llama Stack defines and standardizes the core building blocks that simplify AI application development. It codified best practices across the Llama ecosystem. More specifically, it provides -We focus on making it easy to build production applications with the Llama model family - from the latest Llama 3.3 to specialized models like Llama Guard for safety. +- **Unified API layer** for Inference, RAG, Agents, Tools, Safety, Evals, and Telemetry. +- **Plugin architecture** to support the rich ecosystem of implementations of the different APIs in different environments like local development, on-premises, cloud, and mobile. +- **Prepackaged verified distributions** which offer a one-stop solution for developers to get started quickly and reliably in any environment +- **Multiple developer interfaces** like CLI and SDKs for Python, Node, iOS, and Android +- **Standalone applications** as examples for how to build production-grade AI applications with Llama Stack
-## Key Features +### Llama Stack Benefits +- **Flexible Options**: Developers can choose their preferred infrastructure without changing APIs and enjoy flexible deployment choice. +- **Consistent Experience**: With its unified APIs Llama Stack makes it easier to build, test, and deploy AI applications with consistent application behavior. +- **Robust Ecosystem**: Llama Stack is already integrated with distribution partners (cloud providers, hardware vendors, and AI-focused companies) that offer tailored infrastructure, software, and services for deploying Llama models. -- **Unified API Layer** for: - - Inference: Run LLM models efficiently - - Safety: Apply content filtering and safety policies - - DatasetIO: Store and retrieve knowledge for RAG - - Agents: Build multi-step agentic workflows - - Evaluation: Test and improve model and agent quality - - Telemetry: Collect and analyze usage data and complex agentic traces - - Post Training ( Coming Soon ): Fine tune models for specific use cases +By reducing friction and complexity, Llama Stack empowers developers to focus on what they do best: building transformative generative AI applications. -- **Rich Provider Ecosystem** - - Local Development: Meta's Reference,Ollama, vLLM, TGI - - Self-hosted: Chroma, pgvector, Nvidia NIM - - Cloud: Fireworks, Together, Nvidia, AWS Bedrock, Groq, Cerebras - - On-device: iOS and Android support - -- **Built for Production** - - Pre-packaged distributions for common deployment scenarios - - Comprehensive evaluation capabilities - - Full observability and monitoring - - Provider federation and fallback - - -## Supported Llama Stack Implementations ### API Providers +Here is a list of the various API providers and available distributions to developers started easily, + | **API Provider Builder** | **Environments** | **Agents** | **Inference** | **Memory** | **Safety** | **Telemetry** | |:------------------------------------------------------------------------------------------:|:----------------------:|:------------------:|:------------------:|:------------------:|:------------------:|:------------------:| | Meta Reference | Single Node | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | @@ -76,7 +64,7 @@ A Llama Stack Distribution (or "distro") is a pre-configured bundle of provider | Fireworks | [llamastack/distribution-fireworks](https://hub.docker.com/repository/docker/llamastack/distribution-fireworks/general) | [Guide](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/fireworks.html) | | vLLM | [llamastack/distribution-remote-vllm](https://hub.docker.com/repository/docker/llamastack/distribution-remote-vllm/general) | [Guide](https://llama-stack.readthedocs.io/en/latest/distributions/self_hosted_distro/remote-vllm.html) | -## Installation +### Installation You have two ways to install this repository: @@ -101,7 +89,7 @@ You have two ways to install this repository: pip install -e . ``` -## Documentation +### Documentation Please checkout our [Documentation](https://llama-stack.readthedocs.io/en/latest/index.html) page for more details. @@ -115,7 +103,7 @@ Please checkout our [Documentation](https://llama-stack.readthedocs.io/en/latest * [Contributing](CONTRIBUTING.md) * [Adding a new API Provider](https://llama-stack.readthedocs.io/en/latest/contributing/new_api_provider.html) to walk-through how to add a new API provider. -## Llama Stack Client SDKs +### Llama Stack Client SDKs | **Language** | **Client SDK** | **Package** | | :----: | :----: | :----: | From d0be9288a380d4d0c0deaff57e9d0331229d9206 Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 23 Jan 2025 12:04:06 -0800 Subject: [PATCH 106/107] Llama_Stack_Building_AI_Applications.ipynb -> getting_started.ipynb (#854) Llama_Stack_Building_AI_Applications.ipynb -> getting_started.ipynb --- .github/workflows/publish-to-test-pypi.yml | 2 +- README.md | 4 ++-- ...k_Building_AI_Applications.ipynb => getting_started.ipynb} | 0 docs/source/building_applications/telemetry.md | 2 +- docs/source/building_applications/tools.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename docs/{notebooks/Llama_Stack_Building_AI_Applications.ipynb => getting_started.ipynb} (100%) diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml index 9fe502254..2e8aaab23 100644 --- a/.github/workflows/publish-to-test-pypi.yml +++ b/.github/workflows/publish-to-test-pypi.yml @@ -238,7 +238,7 @@ jobs: run: | pip install pytest nbval llama stack build --template together --image-type venv - pytest -v -s --nbval-lax ./docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb + pytest -v -s --nbval-lax ./docs/getting_started.ipynb pytest -v -s --nbval-lax ./docs/notebooks/Llama_Stack_Benchmark_Evals.ipynb # TODO: add trigger for integration test workflow & docker builds diff --git a/README.md b/README.md index 9d6009bae..17acd0096 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![PyPI - Downloads](https://img.shields.io/pypi/dm/llama-stack)](https://pypi.org/project/llama-stack/) [![Discord](https://img.shields.io/discord/1257833999603335178)](https://discord.gg/llama-stack) -[**Quick Start**](https://llama-stack.readthedocs.io/en/latest/getting_started/index.html) | [**Documentation**](https://llama-stack.readthedocs.io/en/latest/index.html) | [**Colab Notebook**](./docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb) +[**Quick Start**](https://llama-stack.readthedocs.io/en/latest/getting_started/index.html) | [**Documentation**](https://llama-stack.readthedocs.io/en/latest/index.html) | [**Colab Notebook**](./docs/getting_started.ipynb) Llama Stack defines and standardizes the core building blocks that simplify AI application development. It codified best practices across the Llama ecosystem. More specifically, it provides @@ -97,7 +97,7 @@ Please checkout our [Documentation](https://llama-stack.readthedocs.io/en/latest * Guide using `llama` CLI to work with Llama models (download, study prompts), and building/starting a Llama Stack distribution. * [Getting Started](https://llama-stack.readthedocs.io/en/latest/getting_started/index.html) * Quick guide to start a Llama Stack server. - * [Jupyter notebook](./docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb) to walk-through how to use simple text and vision inference llama_stack_client APIs + * [Jupyter notebook](./docs/getting_started.ipynb) to walk-through how to use simple text and vision inference llama_stack_client APIs * The complete Llama Stack lesson [Colab notebook](https://colab.research.google.com/drive/1dtVmxotBsI4cGZQNsJRYPrLiDeT0Wnwt) of the new [Llama 3.2 course on Deeplearning.ai](https://learn.deeplearning.ai/courses/introducing-multimodal-llama-3-2/lesson/8/llama-stack). * A [Zero-to-Hero Guide](https://github.com/meta-llama/llama-stack/tree/main/docs/zero_to_hero_guide) that guide you through all the key components of llama stack with code samples. * [Contributing](CONTRIBUTING.md) diff --git a/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb b/docs/getting_started.ipynb similarity index 100% rename from docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb rename to docs/getting_started.ipynb diff --git a/docs/source/building_applications/telemetry.md b/docs/source/building_applications/telemetry.md index ee640398b..45bc7a1c2 100644 --- a/docs/source/building_applications/telemetry.md +++ b/docs/source/building_applications/telemetry.md @@ -77,4 +77,4 @@ Once the Jaeger instance is running, you can visualize traces by navigating to h ## Querying Traces Stored in SQLIte -The `sqlite` sink allows you to query traces without an external system. Here are some example queries. Refer to the notebook at [Llama Stack Building AI Applications](https://github.com/meta-llama/llama-stack/blob/main/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb) for more examples on how to query traces and spaces. +The `sqlite` sink allows you to query traces without an external system. Here are some example queries. Refer to the notebook at [Llama Stack Building AI Applications](https://github.com/meta-llama/llama-stack/blob/main/docs/getting_started.ipynb) for more examples on how to query traces and spaces. diff --git a/docs/source/building_applications/tools.md b/docs/source/building_applications/tools.md index 1339a14ae..81b4ab68e 100644 --- a/docs/source/building_applications/tools.md +++ b/docs/source/building_applications/tools.md @@ -7,7 +7,7 @@ Tools are treated as any other resource in llama stack like models. You can regi When instatiating an agent, you can provide it a list of tool groups that it has access to. Agent gets the corresponding tool definitions for the specified tool groups and passes them along to the model. -Refer to the [Building AI Applications](https://github.com/meta-llama/llama-stack/blob/main/docs/notebooks/Llama_Stack_Building_AI_Applications.ipynb) notebook for more examples on how to use tools. +Refer to the [Building AI Applications](https://github.com/meta-llama/llama-stack/blob/main/docs/getting_started.ipynb) notebook for more examples on how to use tools. ## Types of Tool Group providers From 86466b71a90755e07db157d97864141d5561f12a Mon Sep 17 00:00:00 2001 From: Dinesh Yeduguru Date: Thu, 23 Jan 2025 12:05:57 -0800 Subject: [PATCH 107/107] update docs for adding new API providers (#855) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # What does this PR do? update docs for adding new API providers ![Screenshot 2025-01-23 at 11 21 42 AM](https://github.com/user-attachments/assets/0d4621d4-ef7e-43cd-9c4a-3e8e0b49242f) --- docs/source/contributing/new_api_provider.md | 58 +++++++++++++++----- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/docs/source/contributing/new_api_provider.md b/docs/source/contributing/new_api_provider.md index f1b50da98..439021685 100644 --- a/docs/source/contributing/new_api_provider.md +++ b/docs/source/contributing/new_api_provider.md @@ -1,27 +1,57 @@ # Adding a New API Provider -This guide contains references to walk you through adding a new API provider. +This guide will walk you through the process of adding a new API provider to Llama Stack. -1. First, decide which API your provider falls into (e.g. Inference, Safety, Agents, Memory). -2. Decide whether your provider is a remote provider, or inline implementation. A remote provider is a provider that makes a remote request to a service. An inline provider is a provider where implementation is executed locally. Checkout the examples, and follow the structure to add your own API provider. Please find the following code pointers: +## Getting Started - - {repopath}`Remote Providers::llama_stack/providers/remote` - - {repopath}`Inline Providers::llama_stack/providers/inline` +1. **Choose Your API Category** + - Determine which API category your provider belongs to (Inference, Safety, Agents, VectorIO) + - Review the core concepts of Llama Stack in the [concepts guide](../concepts/index.md) -3. [Build a Llama Stack distribution](https://llama-stack.readthedocs.io/en/latest/distributions/building_distro.html) with your API provider. -4. Test your code! +2. **Determine Provider Type** + - **Remote Provider**: Makes requests to external services + - **Inline Provider**: Executes implementation locally -## Testing your newly added API providers + Reference existing implementations: + - {repopath}`Remote Providers::llama_stack/providers/remote` + - {repopath}`Inline Providers::llama_stack/providers/inline` -1. Start with an _integration test_ for your provider. That means we will instantiate the real provider, pass it real configuration and if it is a remote service, we will actually hit the remote service. We **strongly** discourage mocking for these tests at the provider level. Llama Stack is first and foremost about integration so we need to make sure stuff works end-to-end. See {repopath}`tests/client-sdk` for an example. + Example PRs: + - [Grok Inference Implementation](https://github.com/meta-llama/llama-stack/pull/609) + - [Nvidia Inference Implementation](https://github.com/meta-llama/llama-stack/pull/355) + - [Model context protocol Tool Runtime](https://github.com/meta-llama/llama-stack/pull/665) +3. **Register Your Provider** + - Add your provider to the appropriate {repopath}`Registry::llama_stack/providers/registry/` + - Specify any required pip dependencies -2. In addition, if you want to unit test functionality within your provider, feel free to do so. You can find some tests in {repopath}`llama_stack/providers/tests/inference/test_text_inference.py`. +4. **Integration** + - Update the run.yaml file to include your provider + - To make your provider a default option or create a new distribution, look at the teamplates in {repopath}`llama_stack/templates/` and run {repopath}`llama_stack/scripts/distro_codegen.py` + - Example PRs: + - [Adding Model Context Protocol Tool Runtime](https://github.com/meta-llama/llama-stack/pull/816) -3. Test with a client-server Llama Stack setup. (a) Start a Llama Stack server with your own distribution which includes the new provider. (b) Send a client request to the server. These client scripts can serve as lightweight tests. +## Testing Guidelines -You can find more complex client scripts [llama-stack-apps](https://github.com/meta-llama/llama-stack-apps/tree/main) repo. Note down which scripts works and do not work with your distribution. +### 1. Integration Testing +- Create integration tests that use real provider instances and configurations +- For remote services, test actual API interactions +- Avoid mocking at the provider level +- Reference examples in {repopath}`tests/client-sdk` -## Submit your PR +### 2. Unit Testing (Optional) +- Add unit tests for provider-specific functionality +- See examples in {repopath}`llama_stack/providers/tests/inference/test_text_inference.py` -After you have fully tested your newly added API provider, submit a PR with the attached test plan. You must have a Test Plan in the summary section of your PR. +### 3. End-to-End Testing +1. Start a Llama Stack server with your new provider +2. Test using client requests +3. Verify compatibility with existing client scripts in the [llama-stack-apps](https://github.com/meta-llama/llama-stack-apps/tree/main) repository +4. Document which scripts are compatible with your provider + +## Submitting Your PR + +1. Ensure all tests pass +2. Include a comprehensive test plan in your PR summary +3. Document any known limitations or considerations +4. Submit your pull request for review