diff --git a/src/crewai/__init__.py b/src/crewai/__init__.py index c4091ec54..795d3248a 100644 --- a/src/crewai/__init__.py +++ b/src/crewai/__init__.py @@ -2,5 +2,6 @@ from crewai.agent import Agent from crewai.crew import Crew from crewai.process import Process from crewai.task import Task +from crewai.conditional_task import ConditionalTask -__all__ = ["Agent", "Crew", "Process", "Task"] +__all__ = ["Agent", "Crew", "Process", "Task", "ConditionalTask"] diff --git a/src/crewai/conditional_task.py b/src/crewai/conditional_task.py new file mode 100644 index 000000000..19c67879e --- /dev/null +++ b/src/crewai/conditional_task.py @@ -0,0 +1,35 @@ +from typing import Callable, Optional, Any +from crewai.task import Task +from crewai.tasks.task_output import TaskOutput + + +class ConditionalTask(Task): + """ + A task that can be conditionally executed based on the output of another task. + Note: This cannot be the only task you have in your crew and cannot be the first since its needs context from the previous task. + """ + + condition: Optional[Callable[[TaskOutput], bool]] = None + + def __init__( + self, + *args, + condition: Optional[Callable[[TaskOutput], bool]] = None, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.condition = condition + + def should_execute(self, context: Any) -> bool: + """ + Determines whether the conditional task should be executed based on the provided context. + + Args: + context (Any): The context or output from the previous task that will be evaluated by the condition. + + Returns: + bool: True if the task should be executed, False otherwise. + """ + if self.condition: + return self.condition(context) + return True diff --git a/src/crewai/crew.py b/src/crewai/crew.py index e540a2ad9..9154a1735 100644 --- a/src/crewai/crew.py +++ b/src/crewai/crew.py @@ -28,6 +28,8 @@ from crewai.memory.long_term.long_term_memory import LongTermMemory from crewai.memory.short_term.short_term_memory import ShortTermMemory from crewai.process import Process from crewai.task import Task +from crewai.conditional_task import ConditionalTask +from crewai.tasks.output_format import OutputFormat from crewai.tasks.task_output import TaskOutput from crewai.telemetry import Telemetry from crewai.tools.agent_tools import AgentTools @@ -295,6 +297,29 @@ class Crew(BaseModel): return self + @model_validator(mode="after") + def validate_first_task(self) -> "Crew": + """Ensure the first task is not a ConditionalTask.""" + if self.tasks and isinstance(self.tasks[0], ConditionalTask): + raise PydanticCustomError( + "invalid_first_task", + "The first task cannot be a ConditionalTask.", + {}, + ) + return self + + @model_validator(mode="after") + def validate_async_tasks_not_async(self) -> "Crew": + """Ensure the first task is not a ConditionalTask.""" + for task in self.tasks: + if task.async_execution and isinstance(task, ConditionalTask): + raise PydanticCustomError( + "invalid_async_conditional_task", + f"Conditional Task: {task.description} , cannot be executed asynchronously.", # type: ignore # Argument of type "str" cannot be assigned to parameter "message_template" of type "LiteralString" + {}, + ) + return self + @model_validator(mode="after") def validate_async_task_cannot_include_sequential_async_tasks_in_context(self): """ @@ -622,7 +647,35 @@ class Crew(BaseModel): f"No agent available for task: {task.description}. Ensure that either the task has an assigned agent or a manager agent is provided." ) self._log_task_start(task, agent_to_use) + if isinstance(task, ConditionalTask): + if futures: + task_outputs.extend( + self._process_async_tasks(futures, was_replayed) + ) + futures.clear() + previous_output = task_outputs[task_index - 1] if task_outputs else None + if previous_output is not None and not task.should_execute( + previous_output + ): + self._logger.log( + "info", + f"Skipping conditional task: {task.description}", + color="yellow", + ) + skipped_task_output = TaskOutput( + description=task.description, + raw="", + agent=task.agent.role if task.agent else "", + output_format=OutputFormat.RAW, + ) + if not was_replayed: + self._store_execution_log( + task, + skipped_task_output, + task_index, + ) + continue if task.async_execution: context = self._get_context( task, [last_sync_output] if last_sync_output else [] @@ -687,6 +740,34 @@ class Crew(BaseModel): # Add the new tool task.tools.append(new_tool) + def _handle_conditional_task( + self, + task: ConditionalTask, + futures: List[Tuple[Task, Future[TaskOutput], int]], + task_outputs: List[TaskOutput], + task_index: int, + was_replayed: bool, + ) -> bool: + """ + Handle conditional task execution. + + Returns: + bool: True if the task should be executed, False if it should be skipped. + """ + if futures: + task_outputs.extend(self._process_async_tasks(futures, was_replayed)) + futures.clear() + + previous_output = task_outputs[task_index - 1] if task_outputs else None + if previous_output is not None and not task.should_execute(previous_output): + self._logger.log( + "info", + f"Skipping conditional task: {task.description}", + color="yellow", + ) + return False + return True + def _log_task_start(self, task: Task, agent: Optional[BaseAgent]): color = self._logging_color role = agent.role if agent else "None" diff --git a/src/crewai/utilities/printer.py b/src/crewai/utilities/printer.py index 3d065c169..4bdd585a4 100644 --- a/src/crewai/utilities/printer.py +++ b/src/crewai/utilities/printer.py @@ -10,6 +10,8 @@ class Printer: self._print_bold_purple(content) elif color == "bold_blue": self._print_bold_blue(content) + elif color == "yellow": + self._print_yellow(content) else: print(content) @@ -27,3 +29,6 @@ class Printer: def _print_bold_blue(self, content): print("\033[1m\033[94m {}\033[00m".format(content)) + + def _print_yellow(self, content): + print("\033[93m {}\033[00m".format(content)) diff --git a/tests/cassettes/test_replay_task_with_context.yaml b/tests/cassettes/test_replay_task_with_context.yaml new file mode 100644 index 000000000..e7f0352b9 --- /dev/null +++ b/tests/cassettes/test_replay_task_with_context.yaml @@ -0,0 +1,449 @@ +interactions: +- request: + body: !!binary | + CrG5AQokCiIKDHNlcnZpY2UubmFtZRISChBjcmV3QUktdGVsZW1ldHJ5Eoe5AQoSChBjcmV3YWku + dGVsZW1ldHJ5EpACChB1aPb7uNmPUxB3quhKypN4EgjnW4at80oLKCoOVGFzayBFeGVjdXRpb24w + ATnIwfLEssXiF0FI2USctcXiF0ouCghjcmV3X2tleRIiCiA5OGE3ZDIxNDI1MjEwNzY5MzhjYzg3 + Yzc2OWRlZGNkM0oxCgdjcmV3X2lkEiYKJDQzOTNlYjRmLTRiMDMtNDgzOS1hMmMwLWViY2IwNTMy + NDJjNEouCgh0YXNrX2tleRIiCiBhZmE2OThiMjYyZDM1NDNmOWE2MTFlNGQ1MTQ1ZWQ2YUoxCgd0 + YXNrX2lkEiYKJDM1ZTA0MTVkLTlhNzYtNDc5YS1hOGFkLTI2NjJiZDQ0NjRiMXoCGAGFAQABAAAS + ig0KEBXTWyRbS66PohO2H7pX1jQSCBQxHUkWdA7dKgxDcmV3IENyZWF0ZWQwATmAAQ+ftcXiF0E4 + fhGftcXiF0oaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjM2LjBKGgoOcHl0aG9uX3ZlcnNpb24SCAoG + My4xMS41Si4KCGNyZXdfa2V5EiIKIGNhN2MwMTM2ZWM3YmY1ZGU3NWRlNWQyNjY5OWRhM2I0SjEK + B2NyZXdfaWQSJgokYzk5MzVhMDItOGJjYi00YTJlLThhZjktYTU5NWNmMTgyMzYzShwKDGNyZXdf + cHJvY2VzcxIMCgpzZXF1ZW50aWFsShEKC2NyZXdfbWVtb3J5EgIQAEoaChRjcmV3X251bWJlcl9v + Zl90YXNrcxICGAFKGwoVY3Jld19udW1iZXJfb2ZfYWdlbnRzEgIYAUr+BAoLY3Jld19hZ2VudHMS + 7gQK6wRbeyJrZXkiOiAiOGJkMjEzOWI1OTc1MTgxNTA2ZTQxZmQ5YzQ1NjNkNzUiLCAiaWQiOiAi + Yjg4NzA5MjYtYzNiYi00MDUyLWIxYTctNDY3ZDdjNDJmMjVkIiwgInJvbGUiOiAiUmVzZWFyY2hl + ciIsICJnb2FsIjogIk1ha2UgdGhlIGJlc3QgcmVzZWFyY2ggYW5kIGFuYWx5c2lzIG9uIGNvbnRl + bnQgYWJvdXQgQUkgYW5kIEFJIGFnZW50cyIsICJiYWNrc3RvcnkiOiAiWW91J3JlIGFuIGV4cGVy + dCByZXNlYXJjaGVyLCBzcGVjaWFsaXplZCBpbiB0ZWNobm9sb2d5LCBzb2Z0d2FyZSBlbmdpbmVl + cmluZywgQUkgYW5kIHN0YXJ0dXBzLiBZb3Ugd29yayBhcyBhIGZyZWVsYW5jZXIgYW5kIGlzIG5v + dyB3b3JraW5nIG9uIGRvaW5nIHJlc2VhcmNoIGFuZCBhbmFseXNpcyBmb3IgYSBuZXcgY3VzdG9t + ZXIuIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDI1LCAibWF4X3JwbSI6IG51bGws + ICJpMThuIjogbnVsbCwgImxsbSI6ICJ7XCJuYW1lXCI6IG51bGwsIFwibW9kZWxfbmFtZVwiOiBc + ImdwdC00b1wiLCBcInRlbXBlcmF0dXJlXCI6IDAuNywgXCJjbGFzc1wiOiBcIkNoYXRPcGVuQUlc + In0iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAidG9vbHNfbmFtZXMiOiBbXX1dSskD + CgpjcmV3X3Rhc2tzEroDCrcDW3sia2V5IjogIjk0NGFlZjBiYWM4NDBmMWMyN2JkODNhOTM3YmMz + NjFiIiwgImlkIjogIjVkZDRjMTJhLThmOWItNDRmNi1iYjRhLWVkODdiNTgzZGU1MCIsICJkZXNj + cmlwdGlvbiI6ICJHaXZlIG1lIGEgbGlzdCBvZiA1IGludGVyZXN0aW5nIGlkZWFzIHRvIGV4cGxv + cmUgZm9yIG5hIGFydGljbGUsIHdoYXQgbWFrZXMgdGhlbSB1bmlxdWUgYW5kIGludGVyZXN0aW5n + LiIsICJleHBlY3RlZF9vdXRwdXQiOiAiQnVsbGV0IHBvaW50IGxpc3Qgb2YgNSBpbXBvcnRhbnQg + ZXZlbnRzLiIsICJhc3luY19leGVjdXRpb24/IjogdHJ1ZSwgImh1bWFuX2lucHV0PyI6IGZhbHNl + LCAiYWdlbnRfcm9sZSI6ICJSZXNlYXJjaGVyIiwgImFnZW50X2tleSI6ICI4YmQyMTM5YjU5NzUx + ODE1MDZlNDFmZDljNDU2M2Q3NSIsICJjb250ZXh0IjogbnVsbCwgInRvb2xzX25hbWVzIjogW119 + XUoqCghwbGF0Zm9ybRIeChxtYWNPUy0xNC4xLjEtYXJtNjQtYXJtLTY0Yml0ShwKEHBsYXRmb3Jt + X3JlbGVhc2USCAoGMjMuMS4wShsKD3BsYXRmb3JtX3N5c3RlbRIICgZEYXJ3aW5KewoQcGxhdGZv + cm1fdmVyc2lvbhJnCmVEYXJ3aW4gS2VybmVsIFZlcnNpb24gMjMuMS4wOiBNb24gT2N0ICA5IDIx + OjI3OjI0IFBEVCAyMDIzOyByb290OnhudS0xMDAwMi40MS45fjYvUkVMRUFTRV9BUk02NF9UNjAw + MEoKCgRjcHVzEgIYCnoCGAGFAQABAAASjgIKEIq7Mjq32M5Kt5dKJnjskZ0SCMx1TtvU1+T2KgxU + YXNrIENyZWF0ZWQwATkI9iiftcXiF0HAfimftcXiF0ouCghjcmV3X2tleRIiCiBjYTdjMDEzNmVj + N2JmNWRlNzVkZTVkMjY2OTlkYTNiNEoxCgdjcmV3X2lkEiYKJGM5OTM1YTAyLThiY2ItNGEyZS04 + YWY5LWE1OTVjZjE4MjM2M0ouCgh0YXNrX2tleRIiCiA5NDRhZWYwYmFjODQwZjFjMjdiZDgzYTkz + N2JjMzYxYkoxCgd0YXNrX2lkEiYKJDVkZDRjMTJhLThmOWItNDRmNi1iYjRhLWVkODdiNTgzZGU1 + MHoCGAGFAQABAAASkAIKEE4G7Oi0pafO3nARt5VKez4SCM6Ua5kmBYFDKg5UYXNrIEV4ZWN1dGlv + bjABOXC1KZ+1xeIXQWDhMJ+1xeIXSi4KCGNyZXdfa2V5EiIKIGNhN2MwMTM2ZWM3YmY1ZGU3NWRl + NWQyNjY5OWRhM2I0SjEKB2NyZXdfaWQSJgokYzk5MzVhMDItOGJjYi00YTJlLThhZjktYTU5NWNm + MTgyMzYzSi4KCHRhc2tfa2V5EiIKIDk0NGFlZjBiYWM4NDBmMWMyN2JkODNhOTM3YmMzNjFiSjEK + B3Rhc2tfaWQSJgokNWRkNGMxMmEtOGY5Yi00NGY2LWJiNGEtZWQ4N2I1ODNkZTUwegIYAYUBAAEA + ABLUGQoQX+5PRYVQ3O7KzTa1tWKiRxIIuu8x8CYW2KEqDENyZXcgQ3JlYXRlZDABOUjW1qO1xeIX + QUjE2aO1xeIXShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuMzYuMEoaCg5weXRob25fdmVyc2lvbhII + CgYzLjExLjVKLgoIY3Jld19rZXkSIgogYTBhYzk1MzU0ZDMyOWJhN2Y2OTQ4M2FkZmUwYzdkMjhK + MQoHY3Jld19pZBImCiQ5MzgzYzU2Yy1lNzQ3LTQwYmItOTlhYi01NGE2NGRkY2Q3YTlKHAoMY3Jl + d19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdfbnVtYmVy + X29mX3Rhc2tzEgIYBEobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgCSvcHCgtjcmV3X2FnZW50 + cxLnBwrkB1t7ImtleSI6ICJkYWI1MjFlYmQzMWFlMTJmZWUxODlhMzhkMzA5MGQ5MyIsICJpZCI6 + ICJhMzAzNTliZi01Y2QwLTQ3MGYtOTk2NS0yYjZhZTU0YmVmY2MiLCAicm9sZSI6ICJXcml0ZXIi + LCAiZ29hbCI6ICJZb3Ugd3JpdGUgbGVzc3NvbnMgb2YgbWF0aCBmb3Iga2lkcy4iLCAiYmFja3N0 + b3J5IjogIllvdSdyZSBhbiBleHBlcnQgaW4gd3JpdHRpbmcgYW5kIHlvdSBsb3ZlIHRvIHRlYWNo + IGtpZHMgYnV0IHlvdSBrbm93IG5vdGhpbmcgb2YgbWF0aC4iLCAidmVyYm9zZT8iOiBmYWxzZSwg + Im1heF9pdGVyIjogMjUsICJtYXhfcnBtIjogbnVsbCwgImkxOG4iOiBudWxsLCAibGxtIjogIntc + Im5hbWVcIjogbnVsbCwgXCJtb2RlbF9uYW1lXCI6IFwiZ3B0LTRvXCIsIFwidGVtcGVyYXR1cmVc + IjogMC43LCBcImNsYXNzXCI6IFwiQ2hhdE9wZW5BSVwifSIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/ + IjogZmFsc2UsICJ0b29sc19uYW1lcyI6IFsibXVsdGlwbGNhdGlvbl90b29sIl19LCB7ImtleSI6 + ICJkYWI1MjFlYmQzMWFlMTJmZWUxODlhMzhkMzA5MGQ5MyIsICJpZCI6ICJhN2JiNTI4ZS0yM2M4 + LTQ1MzItYjNiYS0yOWU1NDNiMGFhN2YiLCAicm9sZSI6ICJXcml0ZXIiLCAiZ29hbCI6ICJZb3Ug + d3JpdGUgbGVzc3NvbnMgb2YgbWF0aCBmb3Iga2lkcy4iLCAiYmFja3N0b3J5IjogIllvdSdyZSBh + biBleHBlcnQgaW4gd3JpdHRpbmcgYW5kIHlvdSBsb3ZlIHRvIHRlYWNoIGtpZHMgYnV0IHlvdSBr + bm93IG5vdGhpbmcgb2YgbWF0aC4iLCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjUs + ICJtYXhfcnBtIjogbnVsbCwgImkxOG4iOiBudWxsLCAibGxtIjogIntcIm5hbWVcIjogbnVsbCwg + XCJtb2RlbF9uYW1lXCI6IFwiZ3B0LTRvXCIsIFwidGVtcGVyYXR1cmVcIjogMC43LCBcImNsYXNz + XCI6IFwiQ2hhdE9wZW5BSVwifSIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJ0b29s + c19uYW1lcyI6IFsibXVsdGlwbGNhdGlvbl90b29sIl19XUqaDQoKY3Jld190YXNrcxKLDQqIDVt7 + ImtleSI6ICIzMGYzMjg2M2EyZWI3OThkMTA5NmM5MDcwMjgwOTgzMCIsICJpZCI6ICIxNjhlOWNm + Ny1hYWJjLTRkMGUtYTg1OS01ODEyZGM4MDg4NTYiLCAiZGVzY3JpcHRpb24iOiAiV2hhdCBpcyAy + IHRpbWVzIDY/IFJldHVybiBvbmx5IHRoZSBudW1iZXIgYWZ0ZXIgdXNpbmcgdGhlIG11bHRpcGxp + Y2F0aW9uIHRvb2wuIiwgImV4cGVjdGVkX291dHB1dCI6ICJ0aGUgcmVzdWx0IG9mIG11bHRpcGxp + Y2F0aW9uIiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNl + LCAiYWdlbnRfcm9sZSI6ICJXcml0ZXIiLCAiYWdlbnRfa2V5IjogImRhYjUyMWViZDMxYWUxMmZl + ZTE4OWEzOGQzMDkwZDkzIiwgImNvbnRleHQiOiBudWxsLCAidG9vbHNfbmFtZXMiOiBbIm11bHRp + cGxjYXRpb25fdG9vbCJdfSwgeyJrZXkiOiAiM2QwYmRlZTMxMjdhZjk5MGIzNjZjMTJkZGJkNGE4 + YTYiLCAiaWQiOiAiMDdkYmU0MzAtZDMxNC00MjgwLWEwODUtOGQ0MTQyZjNjZTFlIiwgImRlc2Ny + aXB0aW9uIjogIldoYXQgaXMgMyB0aW1lcyAxPyBSZXR1cm4gb25seSB0aGUgbnVtYmVyIGFmdGVy + IHVzaW5nIHRoZSBtdWx0aXBsaWNhdGlvbiB0b29sLiIsICJleHBlY3RlZF9vdXRwdXQiOiAidGhl + IHJlc3VsdCBvZiBtdWx0aXBsaWNhdGlvbiIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJo + dW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiV3JpdGVyIiwgImFnZW50X2tleSI6 + ICJkYWI1MjFlYmQzMWFlMTJmZWUxODlhMzhkMzA5MGQ5MyIsICJjb250ZXh0IjogbnVsbCwgInRv + b2xzX25hbWVzIjogWyJtdWx0aXBsY2F0aW9uX3Rvb2wiXX0sIHsia2V5IjogIjMwZjMyODYzYTJl + Yjc5OGQxMDk2YzkwNzAyODA5ODMwIiwgImlkIjogImFlYjJmMjk1LTI3NDUtNGYwMi05MjQxLWU3 + NTk2MjVjOWQyMCIsICJkZXNjcmlwdGlvbiI6ICJXaGF0IGlzIDIgdGltZXMgNj8gUmV0dXJuIG9u + bHkgdGhlIG51bWJlciBhZnRlciB1c2luZyB0aGUgbXVsdGlwbGljYXRpb24gdG9vbC4iLCAiZXhw + ZWN0ZWRfb3V0cHV0IjogInRoZSByZXN1bHQgb2YgbXVsdGlwbGljYXRpb24iLCAiYXN5bmNfZXhl + Y3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogIldy + aXRlciIsICJhZ2VudF9rZXkiOiAiZGFiNTIxZWJkMzFhZTEyZmVlMTg5YTM4ZDMwOTBkOTMiLCAi + Y29udGV4dCI6IG51bGwsICJ0b29sc19uYW1lcyI6IFsibXVsdGlwbGNhdGlvbl90b29sIl19LCB7 + ImtleSI6ICIzZDBiZGVlMzEyN2FmOTkwYjM2NmMxMmRkYmQ0YThhNiIsICJpZCI6ICI5MGE0N2Fm + Ny1jZjc5LTQ3NjYtOWJjNS02Nzg3MWMwOTFiOTYiLCAiZGVzY3JpcHRpb24iOiAiV2hhdCBpcyAz + IHRpbWVzIDE/IFJldHVybiBvbmx5IHRoZSBudW1iZXIgYWZ0ZXIgdXNpbmcgdGhlIG11bHRpcGxp + Y2F0aW9uIHRvb2wuIiwgImV4cGVjdGVkX291dHB1dCI6ICJ0aGUgcmVzdWx0IG9mIG11bHRpcGxp + Y2F0aW9uIiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNl + LCAiYWdlbnRfcm9sZSI6ICJXcml0ZXIiLCAiYWdlbnRfa2V5IjogImRhYjUyMWViZDMxYWUxMmZl + ZTE4OWEzOGQzMDkwZDkzIiwgImNvbnRleHQiOiBudWxsLCAidG9vbHNfbmFtZXMiOiBbIm11bHRp + cGxjYXRpb25fdG9vbCJdfV1KKgoIcGxhdGZvcm0SHgocbWFjT1MtMTQuMS4xLWFybTY0LWFybS02 + NGJpdEocChBwbGF0Zm9ybV9yZWxlYXNlEggKBjIzLjEuMEobCg9wbGF0Zm9ybV9zeXN0ZW0SCAoG + RGFyd2luSnsKEHBsYXRmb3JtX3ZlcnNpb24SZwplRGFyd2luIEtlcm5lbCBWZXJzaW9uIDIzLjEu + MDogTW9uIE9jdCAgOSAyMToyNzoyNCBQRFQgMjAyMzsgcm9vdDp4bnUtMTAwMDIuNDEuOX42L1JF + TEVBU0VfQVJNNjRfVDYwMDBKCgoEY3B1cxICGAp6AhgBhQEAAQAAEo4CChDMsDVWCtNnTZd78P6M + F0qHEghu/cLCLfIljioMVGFzayBDcmVhdGVkMAE5ALHzo7XF4hdBMCb0o7XF4hdKLgoIY3Jld19r + ZXkSIgogYTBhYzk1MzU0ZDMyOWJhN2Y2OTQ4M2FkZmUwYzdkMjhKMQoHY3Jld19pZBImCiQ5Mzgz + YzU2Yy1lNzQ3LTQwYmItOTlhYi01NGE2NGRkY2Q3YTlKLgoIdGFza19rZXkSIgogMzBmMzI4NjNh + MmViNzk4ZDEwOTZjOTA3MDI4MDk4MzBKMQoHdGFza19pZBImCiQxNjhlOWNmNy1hYWJjLTRkMGUt + YTg1OS01ODEyZGM4MDg4NTZ6AhgBhQEAAQAAEpUBChB1ZPMBfoWizT2O65qegWcGEgjmR3EI+Dhz + KSoKVG9vbCBVc2FnZTABOUCLN6W1xeIXQWBWOKW1xeIXShoKDmNyZXdhaV92ZXJzaW9uEggKBjAu + MzYuMEohCgl0b29sX25hbWUSFAoSbXVsdGlwbGNhdGlvbl90b29sSg4KCGF0dGVtcHRzEgIYAXoC + GAGFAQABAAASkAIKEEukGnCGulTfJ1/e50UIF5ASCBeMmMmX2ss5Kg5UYXNrIEV4ZWN1dGlvbjAB + OShR9KO1xeIXQQBL+KW1xeIXSi4KCGNyZXdfa2V5EiIKIGEwYWM5NTM1NGQzMjliYTdmNjk0ODNh + ZGZlMGM3ZDI4SjEKB2NyZXdfaWQSJgokOTM4M2M1NmMtZTc0Ny00MGJiLTk5YWItNTRhNjRkZGNk + N2E5Si4KCHRhc2tfa2V5EiIKIDMwZjMyODYzYTJlYjc5OGQxMDk2YzkwNzAyODA5ODMwSjEKB3Rh + c2tfaWQSJgokMTY4ZTljZjctYWFiYy00ZDBlLWE4NTktNTgxMmRjODA4ODU2egIYAYUBAAEAABKO + AgoQeq27u45vVnfO3QW8y5TkKhII8pwpZBclUSUqDFRhc2sgQ3JlYXRlZDABOXCRBaa1xeIXQeAl + Bqa1xeIXSi4KCGNyZXdfa2V5EiIKIGEwYWM5NTM1NGQzMjliYTdmNjk0ODNhZGZlMGM3ZDI4SjEK + B2NyZXdfaWQSJgokOTM4M2M1NmMtZTc0Ny00MGJiLTk5YWItNTRhNjRkZGNkN2E5Si4KCHRhc2tf + a2V5EiIKIDNkMGJkZWUzMTI3YWY5OTBiMzY2YzEyZGRiZDRhOGE2SjEKB3Rhc2tfaWQSJgokMDdk + YmU0MzAtZDMxNC00MjgwLWEwODUtOGQ0MTQyZjNjZTFlegIYAYUBAAEAABKVAQoQDpoau444Nc2P + TpqGSwuJUBII3odk2PVsECQqClRvb2wgVXNhZ2UwATlQXjmntcXiF0GwSDqntcXiF0oaCg5jcmV3 + YWlfdmVyc2lvbhIICgYwLjM2LjBKIQoJdG9vbF9uYW1lEhQKEm11bHRpcGxjYXRpb25fdG9vbEoO + CghhdHRlbXB0cxICGAF6AhgBhQEAAQAAEpACChBW89qgKwxZoE48iGgYORjmEggn0gOg6T0j3SoO + VGFzayBFeGVjdXRpb24wATnYUAamtcXiF0GwUfSntcXiF0ouCghjcmV3X2tleRIiCiBhMGFjOTUz + NTRkMzI5YmE3ZjY5NDgzYWRmZTBjN2QyOEoxCgdjcmV3X2lkEiYKJDkzODNjNTZjLWU3NDctNDBi + Yi05OWFiLTU0YTY0ZGRjZDdhOUouCgh0YXNrX2tleRIiCiAzZDBiZGVlMzEyN2FmOTkwYjM2NmMx + MmRkYmQ0YThhNkoxCgd0YXNrX2lkEiYKJDA3ZGJlNDMwLWQzMTQtNDI4MC1hMDg1LThkNDE0MmYz + Y2UxZXoCGAGFAQABAAASjgIKECX4kiD6RrdHOsL/u1WPaKISCEn8hmiuaOxzKgxUYXNrIENyZWF0 + ZWQwATngAQCotcXiF0GwhgCotcXiF0ouCghjcmV3X2tleRIiCiBhMGFjOTUzNTRkMzI5YmE3ZjY5 + NDgzYWRmZTBjN2QyOEoxCgdjcmV3X2lkEiYKJDkzODNjNTZjLWU3NDctNDBiYi05OWFiLTU0YTY0 + ZGRjZDdhOUouCgh0YXNrX2tleRIiCiAzMGYzMjg2M2EyZWI3OThkMTA5NmM5MDcwMjgwOTgzMEox + Cgd0YXNrX2lkEiYKJGFlYjJmMjk1LTI3NDUtNGYwMi05MjQxLWU3NTk2MjVjOWQyMHoCGAGFAQAB + AAASlQEKEA/spc2u3GsQSPtfjOb6NJISCG3tEmcOqAoDKgpUb29sIFVzYWdlMAE58GZDqbXF4hdB + IFlEqbXF4hdKGgoOY3Jld2FpX3ZlcnNpb24SCAoGMC4zNi4wSiEKCXRvb2xfbmFtZRIUChJtdWx0 + aXBsY2F0aW9uX3Rvb2xKDgoIYXR0ZW1wdHMSAhgBegIYAYUBAAEAABKQAgoQsO+zsd47DClQTmS7 + ZfZYLxII2UxCduX9osEqDlRhc2sgRXhlY3V0aW9uMAE5qLEAqLXF4hdByLEBqrXF4hdKLgoIY3Jl + d19rZXkSIgogYTBhYzk1MzU0ZDMyOWJhN2Y2OTQ4M2FkZmUwYzdkMjhKMQoHY3Jld19pZBImCiQ5 + MzgzYzU2Yy1lNzQ3LTQwYmItOTlhYi01NGE2NGRkY2Q3YTlKLgoIdGFza19rZXkSIgogMzBmMzI4 + NjNhMmViNzk4ZDEwOTZjOTA3MDI4MDk4MzBKMQoHdGFza19pZBImCiRhZWIyZjI5NS0yNzQ1LTRm + MDItOTI0MS1lNzU5NjI1YzlkMjB6AhgBhQEAAQAAEo4CChD55L/w1V+ezIjuajyG1hwUEggBdDur + +CTqUCoMVGFzayBDcmVhdGVkMAE52AcPqrXF4hdBSJwPqrXF4hdKLgoIY3Jld19rZXkSIgogYTBh + Yzk1MzU0ZDMyOWJhN2Y2OTQ4M2FkZmUwYzdkMjhKMQoHY3Jld19pZBImCiQ5MzgzYzU2Yy1lNzQ3 + LTQwYmItOTlhYi01NGE2NGRkY2Q3YTlKLgoIdGFza19rZXkSIgogM2QwYmRlZTMxMjdhZjk5MGIz + NjZjMTJkZGJkNGE4YTZKMQoHdGFza19pZBImCiQ5MGE0N2FmNy1jZjc5LTQ3NjYtOWJjNS02Nzg3 + MWMwOTFiOTZ6AhgBhQEAAQAAEpUBChA+cdY4kcsaUDolyJw3c952EggtUbX8zXBaySoKVG9vbCBV + c2FnZTABOaAHUKu1xeIXQcDSUKu1xeIXShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuMzYuMEohCgl0 + b29sX25hbWUSFAoSbXVsdGlwbGNhdGlvbl90b29sSg4KCGF0dGVtcHRzEgIYAXoCGAGFAQABAAAS + kAIKEAvnVK0WhsX8u4xShcr/sooSCC+aS9IRpThCKg5UYXNrIEV4ZWN1dGlvbjABOUDHD6q1xeIX + QVC9IKy1xeIXSi4KCGNyZXdfa2V5EiIKIGEwYWM5NTM1NGQzMjliYTdmNjk0ODNhZGZlMGM3ZDI4 + SjEKB2NyZXdfaWQSJgokOTM4M2M1NmMtZTc0Ny00MGJiLTk5YWItNTRhNjRkZGNkN2E5Si4KCHRh + c2tfa2V5EiIKIDNkMGJkZWUzMTI3YWY5OTBiMzY2YzEyZGRiZDRhOGE2SjEKB3Rhc2tfaWQSJgok + OTBhNDdhZjctY2Y3OS00NzY2LTliYzUtNjc4NzFjMDkxYjk2egIYAYUBAAEAABKrCwoQfae3w+r8 + n0qGJnp7DP6RtxIILhPziA5VzRwqDENyZXcgQ3JlYXRlZDABOZipkrW1xeIXQZDIlLW1xeIXShoK + DmNyZXdhaV92ZXJzaW9uEggKBjAuMzYuMEoaCg5weXRob25fdmVyc2lvbhIICgYzLjExLjVKLgoI + Y3Jld19rZXkSIgogYzk3YjVmZWI1ZDFiNjZiYjU5MDA2YWFhMDFhMjljZDZKMQoHY3Jld19pZBIm + CiQ2M2Y2MGU1Yi1kMjZkLTQwZjMtYTQyMC0wNzQ3Y2MwOWM5YWRKHAoMY3Jld19wcm9jZXNzEgwK + CnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhABShoKFGNyZXdfbnVtYmVyX29mX3Rhc2tzEgIY + AUobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgBStIDCgtjcmV3X2FnZW50cxLCAwq/A1t7Imtl + eSI6ICIwN2Q5OWI2MzA0MTFkMzVmZDkwNDdhNTMyZDUzZGRhNyIsICJpZCI6ICJhMGJlYjc4ZS04 + ZGUzLTQ5YjEtYWRkZS1mODg3NWMzNjM3ZGYiLCAicm9sZSI6ICJSZXNlYXJjaGVyIiwgImdvYWwi + OiAiWW91IHJlc2VhcmNoIGFib3V0IG1hdGguIiwgImJhY2tzdG9yeSI6ICJZb3UncmUgYW4gZXhw + ZXJ0IGluIHJlc2VhcmNoIGFuZCB5b3UgbG92ZSB0byBsZWFybiBuZXcgdGhpbmdzLiIsICJ2ZXJi + b3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyNSwgIm1heF9ycG0iOiBudWxsLCAiaTE4biI6IG51 + bGwsICJsbG0iOiAie1wibmFtZVwiOiBudWxsLCBcIm1vZGVsX25hbWVcIjogXCJncHQtNG9cIiwg + XCJ0ZW1wZXJhdHVyZVwiOiAwLjcsIFwiY2xhc3NcIjogXCJDaGF0T3BlbkFJXCJ9IiwgImRlbGVn + YXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgInRvb2xzX25hbWVzIjogW119XUqWAwoKY3Jld190YXNr + cxKHAwqEA1t7ImtleSI6ICI2Mzk5NjUxN2YzZjNmMWM5NGQ2YmI2MTdhYTBiMWM0ZiIsICJpZCI6 + ICIwMmNiOWIyYS0yODIyLTQwNTYtYjQ5MC04YjA0YzA2MTg2M2IiLCAiZGVzY3JpcHRpb24iOiAi + UmVzZWFyY2ggYSB0b3BpYyB0byB0ZWFjaCBhIGtpZCBhZ2VkIDYgYWJvdXQgbWF0aC4iLCAiZXhw + ZWN0ZWRfb3V0cHV0IjogIkEgdG9waWMsIGV4cGxhbmF0aW9uLCBhbmdsZSwgYW5kIGV4YW1wbGVz + LiIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFn + ZW50X3JvbGUiOiAiUmVzZWFyY2hlciIsICJhZ2VudF9rZXkiOiAiMDdkOTliNjMwNDExZDM1ZmQ5 + MDQ3YTUzMmQ1M2RkYTciLCAiY29udGV4dCI6IG51bGwsICJ0b29sc19uYW1lcyI6IFtdfV1KKgoI + cGxhdGZvcm0SHgocbWFjT1MtMTQuMS4xLWFybTY0LWFybS02NGJpdEocChBwbGF0Zm9ybV9yZWxl + YXNlEggKBjIzLjEuMEobCg9wbGF0Zm9ybV9zeXN0ZW0SCAoGRGFyd2luSnsKEHBsYXRmb3JtX3Zl + cnNpb24SZwplRGFyd2luIEtlcm5lbCBWZXJzaW9uIDIzLjEuMDogTW9uIE9jdCAgOSAyMToyNzoy + NCBQRFQgMjAyMzsgcm9vdDp4bnUtMTAwMDIuNDEuOX42L1JFTEVBU0VfQVJNNjRfVDYwMDBKCgoE + Y3B1cxICGAp6AhgBhQEAAQAAEo4CChDwvE8C6FMSvgaW9DsUngzEEgi69DWPEIv7/ioMVGFzayBD + cmVhdGVkMAE5QJOmtbXF4hdBSOWmtbXF4hdKLgoIY3Jld19rZXkSIgogYzk3YjVmZWI1ZDFiNjZi + YjU5MDA2YWFhMDFhMjljZDZKMQoHY3Jld19pZBImCiQ2M2Y2MGU1Yi1kMjZkLTQwZjMtYTQyMC0w + NzQ3Y2MwOWM5YWRKLgoIdGFza19rZXkSIgogNjM5OTY1MTdmM2YzZjFjOTRkNmJiNjE3YWEwYjFj + NGZKMQoHdGFza19pZBImCiQwMmNiOWIyYS0yODIyLTQwNTYtYjQ5MC04YjA0YzA2MTg2M2J6AhgB + hQEAAQAAEpACChA9ji0VC42jOLjDFsSGAZehEgi88WVi+J8WZyoOVGFzayBFeGVjdXRpb24wATlA + EKe1tcXiF0GwOXS9tcXiF0ouCghjcmV3X2tleRIiCiBjOTdiNWZlYjVkMWI2NmJiNTkwMDZhYWEw + MWEyOWNkNkoxCgdjcmV3X2lkEiYKJDYzZjYwZTViLWQyNmQtNDBmMy1hNDIwLTA3NDdjYzA5Yzlh + ZEouCgh0YXNrX2tleRIiCiA2Mzk5NjUxN2YzZjNmMWM5NGQ2YmI2MTdhYTBiMWM0ZkoxCgd0YXNr + X2lkEiYKJDAyY2I5YjJhLTI4MjItNDA1Ni1iNDkwLThiMDRjMDYxODYzYnoCGAGFAQABAAASqwsK + EOSKFi2MNd2ArhJhwSRFLU8SCLUBjfAc1xVOKgxDcmV3IENyZWF0ZWQwATlAcNi/tcXiF0G4zdq/ + tcXiF0oaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjM2LjBKGgoOcHl0aG9uX3ZlcnNpb24SCAoGMy4x + MS41Si4KCGNyZXdfa2V5EiIKIGM5N2I1ZmViNWQxYjY2YmI1OTAwNmFhYTAxYTI5Y2Q2SjEKB2Ny + ZXdfaWQSJgokNmFlNzhhZjEtNDE5Ny00ODU2LWE1OTItYWEzZGNmMTI5OGJhShwKDGNyZXdfcHJv + Y2VzcxIMCgpzZXF1ZW50aWFsShEKC2NyZXdfbWVtb3J5EgIQAEoaChRjcmV3X251bWJlcl9vZl90 + YXNrcxICGAFKGwoVY3Jld19udW1iZXJfb2ZfYWdlbnRzEgIYAUrSAwoLY3Jld19hZ2VudHMSwgMK + vwNbeyJrZXkiOiAiMDdkOTliNjMwNDExZDM1ZmQ5MDQ3YTUzMmQ1M2RkYTciLCAiaWQiOiAiYWUz + ZjEwYmYtNThlMy00NjNjLWI4YTUtMDZjNTAxYTVlNWY0IiwgInJvbGUiOiAiUmVzZWFyY2hlciIs + ICJnb2FsIjogIllvdSByZXNlYXJjaCBhYm91dCBtYXRoLiIsICJiYWNrc3RvcnkiOiAiWW91J3Jl + IGFuIGV4cGVydCBpbiByZXNlYXJjaCBhbmQgeW91IGxvdmUgdG8gbGVhcm4gbmV3IHRoaW5ncy4i + LCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjUsICJtYXhfcnBtIjogbnVsbCwgImkx + OG4iOiBudWxsLCAibGxtIjogIntcIm5hbWVcIjogbnVsbCwgXCJtb2RlbF9uYW1lXCI6IFwiZ3B0 + LTRvXCIsIFwidGVtcGVyYXR1cmVcIjogMC43LCBcImNsYXNzXCI6IFwiQ2hhdE9wZW5BSVwifSIs + ICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJ0b29sc19uYW1lcyI6IFtdfV1KlgMKCmNy + ZXdfdGFza3MShwMKhANbeyJrZXkiOiAiNjM5OTY1MTdmM2YzZjFjOTRkNmJiNjE3YWEwYjFjNGYi + LCAiaWQiOiAiNTQ4NzY5YWUtMmFjZS00NzIwLTlhYWEtZWY3YmVjZGRmMjAyIiwgImRlc2NyaXB0 + aW9uIjogIlJlc2VhcmNoIGEgdG9waWMgdG8gdGVhY2ggYSBraWQgYWdlZCA2IGFib3V0IG1hdGgu + IiwgImV4cGVjdGVkX291dHB1dCI6ICJBIHRvcGljLCBleHBsYW5hdGlvbiwgYW5nbGUsIGFuZCBl + eGFtcGxlcy4iLCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFs + c2UsICJhZ2VudF9yb2xlIjogIlJlc2VhcmNoZXIiLCAiYWdlbnRfa2V5IjogIjA3ZDk5YjYzMDQx + MWQzNWZkOTA0N2E1MzJkNTNkZGE3IiwgImNvbnRleHQiOiBudWxsLCAidG9vbHNfbmFtZXMiOiBb + XX1dSioKCHBsYXRmb3JtEh4KHG1hY09TLTE0LjEuMS1hcm02NC1hcm0tNjRiaXRKHAoQcGxhdGZv + cm1fcmVsZWFzZRIICgYyMy4xLjBKGwoPcGxhdGZvcm1fc3lzdGVtEggKBkRhcndpbkp7ChBwbGF0 + Zm9ybV92ZXJzaW9uEmcKZURhcndpbiBLZXJuZWwgVmVyc2lvbiAyMy4xLjA6IE1vbiBPY3QgIDkg + MjE6Mjc6MjQgUERUIDIwMjM7IHJvb3Q6eG51LTEwMDAyLjQxLjl+Ni9SRUxFQVNFX0FSTTY0X1Q2 + MDAwSgoKBGNwdXMSAhgKegIYAYUBAAEAABKOAgoQJwZSCcuSu3diAuOYiVdE2BIIeF1BeIAKnP4q + DFRhc2sgQ3JlYXRlZDABOYhX77+1xeIXQcCh77+1xeIXSi4KCGNyZXdfa2V5EiIKIGM5N2I1ZmVi + NWQxYjY2YmI1OTAwNmFhYTAxYTI5Y2Q2SjEKB2NyZXdfaWQSJgokNmFlNzhhZjEtNDE5Ny00ODU2 + LWE1OTItYWEzZGNmMTI5OGJhSi4KCHRhc2tfa2V5EiIKIDYzOTk2NTE3ZjNmM2YxYzk0ZDZiYjYx + N2FhMGIxYzRmSjEKB3Rhc2tfaWQSJgokNTQ4NzY5YWUtMmFjZS00NzIwLTlhYWEtZWY3YmVjZGRm + MjAyegIYAYUBAAEAABKQAgoQxBgL6L3Ce74Cu4q6j0ErDRIIEnXenwnk/A4qDlRhc2sgRXhlY3V0 + aW9uMAE5uMzvv7XF4hdBAEPaxrXF4hdKLgoIY3Jld19rZXkSIgogYzk3YjVmZWI1ZDFiNjZiYjU5 + MDA2YWFhMDFhMjljZDZKMQoHY3Jld19pZBImCiQ2YWU3OGFmMS00MTk3LTQ4NTYtYTU5Mi1hYTNk + Y2YxMjk4YmFKLgoIdGFza19rZXkSIgogNjM5OTY1MTdmM2YzZjFjOTRkNmJiNjE3YWEwYjFjNGZK + MQoHdGFza19pZBImCiQ1NDg3NjlhZS0yYWNlLTQ3MjAtOWFhYS1lZjdiZWNkZGYyMDJ6AhgBhQEA + AQAAEo0MChB2PHWFIMgXT4sbnL1Fk9tqEgj1iM6d8aLAFyoMQ3JldyBDcmVhdGVkMAE5SKovx7XF + 4hdBwAcyx7XF4hdKGgoOY3Jld2FpX3ZlcnNpb24SCAoGMC4zNi4wShoKDnB5dGhvbl92ZXJzaW9u + EggKBjMuMTEuNUouCghjcmV3X2tleRIiCiA4YzI3NTJmNDllNWI5ZDJiNjhjYjM1Y2FjOGZjYzg2 + ZEoxCgdjcmV3X2lkEiYKJDA2MTdhZjU1LWEwNjQtNDEwMi05YjQ0LThhMThiZWU0NTg3OEocCgxj + cmV3X3Byb2Nlc3MSDAoKc2VxdWVudGlhbEoRCgtjcmV3X21lbW9yeRICEABKGgoUY3Jld19udW1i + ZXJfb2ZfdGFza3MSAhgBShsKFWNyZXdfbnVtYmVyX29mX2FnZW50cxICGAFK/gQKC2NyZXdfYWdl + bnRzEu4ECusEW3sia2V5IjogIjhiZDIxMzliNTk3NTE4MTUwNmU0MWZkOWM0NTYzZDc1IiwgImlk + IjogIjc4YmM1N2I4LWIwZTgtNDZlNy04OTIzLTQ2ZDJlMWQ4MjQwNyIsICJyb2xlIjogIlJlc2Vh + cmNoZXIiLCAiZ29hbCI6ICJNYWtlIHRoZSBiZXN0IHJlc2VhcmNoIGFuZCBhbmFseXNpcyBvbiBj + b250ZW50IGFib3V0IEFJIGFuZCBBSSBhZ2VudHMiLCAiYmFja3N0b3J5IjogIllvdSdyZSBhbiBl + eHBlcnQgcmVzZWFyY2hlciwgc3BlY2lhbGl6ZWQgaW4gdGVjaG5vbG9neSwgc29mdHdhcmUgZW5n + aW5lZXJpbmcsIEFJIGFuZCBzdGFydHVwcy4gWW91IHdvcmsgYXMgYSBmcmVlbGFuY2VyIGFuZCBp + cyBub3cgd29ya2luZyBvbiBkb2luZyByZXNlYXJjaCBhbmQgYW5hbHlzaXMgZm9yIGEgbmV3IGN1 + c3RvbWVyLiIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyNSwgIm1heF9ycG0iOiBu + dWxsLCAiaTE4biI6IG51bGwsICJsbG0iOiAie1wibmFtZVwiOiBudWxsLCBcIm1vZGVsX25hbWVc + IjogXCJncHQtNG9cIiwgXCJ0ZW1wZXJhdHVyZVwiOiAwLjcsIFwiY2xhc3NcIjogXCJDaGF0T3Bl + bkFJXCJ9IiwgImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgInRvb2xzX25hbWVzIjogW119 + XUrMAgoKY3Jld190YXNrcxK9Agq6Alt7ImtleSI6ICIwZDY4NWEyMTk5NGQ5NDkwOTdiYzVhNTZk + NzM3ZTZkMSIsICJpZCI6ICI0M2JjZTUwNy0yZGJmLTRiMDMtYTIyMy0zYzMxMmU1MTI4YWQiLCAi + ZGVzY3JpcHRpb24iOiAiU2F5IEhpIiwgImV4cGVjdGVkX291dHB1dCI6ICJUaGUgd29yZDogSGki + LCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2Vu + dF9yb2xlIjogIlJlc2VhcmNoZXIiLCAiYWdlbnRfa2V5IjogIjhiZDIxMzliNTk3NTE4MTUwNmU0 + MWZkOWM0NTYzZDc1IiwgImNvbnRleHQiOiBudWxsLCAidG9vbHNfbmFtZXMiOiBbXX1dSioKCHBs + YXRmb3JtEh4KHG1hY09TLTE0LjEuMS1hcm02NC1hcm0tNjRiaXRKHAoQcGxhdGZvcm1fcmVsZWFz + ZRIICgYyMy4xLjBKGwoPcGxhdGZvcm1fc3lzdGVtEggKBkRhcndpbkp7ChBwbGF0Zm9ybV92ZXJz + aW9uEmcKZURhcndpbiBLZXJuZWwgVmVyc2lvbiAyMy4xLjA6IE1vbiBPY3QgIDkgMjE6Mjc6MjQg + UERUIDIwMjM7IHJvb3Q6eG51LTEwMDAyLjQxLjl+Ni9SRUxFQVNFX0FSTTY0X1Q2MDAwSgoKBGNw + dXMSAhgKegIYAYUBAAEAABKOAgoQgFFdbbcq9b6XpdCgV4ygHxII5FvLDHBJWfYqDFRhc2sgQ3Jl + YXRlZDABOcB3Sce1xeIXQfDsSce1xeIXSi4KCGNyZXdfa2V5EiIKIDhjMjc1MmY0OWU1YjlkMmI2 + OGNiMzVjYWM4ZmNjODZkSjEKB2NyZXdfaWQSJgokMDYxN2FmNTUtYTA2NC00MTAyLTliNDQtOGEx + OGJlZTQ1ODc4Si4KCHRhc2tfa2V5EiIKIDBkNjg1YTIxOTk0ZDk0OTA5N2JjNWE1NmQ3MzdlNmQx + SjEKB3Rhc2tfaWQSJgokNDNiY2U1MDctMmRiZi00YjAzLWEyMjMtM2MzMTJlNTEyOGFkegIYAYUB + AAEAABKQAgoQrD1woM4vvML78Ycv2kKJPBIIQiPC5tnfBsEqDlRhc2sgRXhlY3V0aW9uMAE5uB9K + x7XF4hdB0DoqyLXF4hdKLgoIY3Jld19rZXkSIgogOGMyNzUyZjQ5ZTViOWQyYjY4Y2IzNWNhYzhm + Y2M4NmRKMQoHY3Jld19pZBImCiQwNjE3YWY1NS1hMDY0LTQxMDItOWI0NC04YTE4YmVlNDU4NzhK + LgoIdGFza19rZXkSIgogMGQ2ODVhMjE5OTRkOTQ5MDk3YmM1YTU2ZDczN2U2ZDFKMQoHdGFza19p + ZBImCiQ0M2JjZTUwNy0yZGJmLTRiMDMtYTIyMy0zYzMxMmU1MTI4YWR6AhgBhQEAAQAAEskSChDy + v1twhKVTS2/hcmcIXG8LEginut5ahEsb9yoMQ3JldyBDcmVhdGVkMAE5eMNWyrXF4hdBUBFZyrXF + 4hdKGgoOY3Jld2FpX3ZlcnNpb24SCAoGMC4zNi4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMuMTEu + NUouCghjcmV3X2tleRIiCiBlM2ZkYTBmMzExMGZlODBiMTg5NDdjMDE0NzE0MzBhNEoxCgdjcmV3 + X2lkEiYKJGJiZmUxOTdkLWFmM2QtNDUwYy04NzlhLWNkOWQyODQyYzk5NUoeCgxjcmV3X3Byb2Nl + c3MSDgoMaGllcmFyY2hpY2FsShEKC2NyZXdfbWVtb3J5EgIQAEoaChRjcmV3X251bWJlcl9vZl90 + YXNrcxICGAFKGwoVY3Jld19udW1iZXJfb2ZfYWdlbnRzEgIYAkrFCQoLY3Jld19hZ2VudHMStQkK + sglbeyJrZXkiOiAiOGJkMjEzOWI1OTc1MTgxNTA2ZTQxZmQ5YzQ1NjNkNzUiLCAiaWQiOiAiNzhi + YzU3YjgtYjBlOC00NmU3LTg5MjMtNDZkMmUxZDgyNDA3IiwgInJvbGUiOiAiUmVzZWFyY2hlciIs + ICJnb2FsIjogIk1ha2UgdGhlIGJlc3QgcmVzZWFyY2ggYW5kIGFuYWx5c2lzIG9uIGNvbnRlbnQg + YWJvdXQgQUkgYW5kIEFJIGFnZW50cyIsICJiYWNrc3RvcnkiOiAiWW91J3JlIGFuIGV4cGVydCBy + ZXNlYXJjaGVyLCBzcGVjaWFsaXplZCBpbiB0ZWNobm9sb2d5LCBzb2Z0d2FyZSBlbmdpbmVlcmlu + ZywgQUkgYW5kIHN0YXJ0dXBzLiBZb3Ugd29yayBhcyBhIGZyZWVsYW5jZXIgYW5kIGlzIG5vdyB3 + b3JraW5nIG9uIGRvaW5nIHJlc2VhcmNoIGFuZCBhbmFseXNpcyBmb3IgYSBuZXcgY3VzdG9tZXIu + IiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDI1LCAibWF4X3JwbSI6IG51bGwsICJp + MThuIjogbnVsbCwgImxsbSI6ICJ7XCJuYW1lXCI6IG51bGwsIFwibW9kZWxfbmFtZVwiOiBcImdw + dC00b1wiLCBcInRlbXBlcmF0dXJlXCI6IDAuNywgXCJjbGFzc1wiOiBcIkNoYXRPcGVuQUlcIn0i + LCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAidG9vbHNfbmFtZXMiOiBbXX0sIHsia2V5 + IjogIjlhNTAxNWVmNDg5NWRjNjI3OGQ1NDgxOGJhNDQ2YWY3IiwgImlkIjogIjk2YzIxOTk1LWFl + ZDItNGJjNC1iYjY4LTdiOTA3ZTYzYjQyNSIsICJyb2xlIjogIlNlbmlvciBXcml0ZXIiLCAiZ29h + bCI6ICJXcml0ZSB0aGUgYmVzdCBjb250ZW50IGFib3V0IEFJIGFuZCBBSSBhZ2VudHMuIiwgImJh + Y2tzdG9yeSI6ICJZb3UncmUgYSBzZW5pb3Igd3JpdGVyLCBzcGVjaWFsaXplZCBpbiB0ZWNobm9s + b2d5LCBzb2Z0d2FyZSBlbmdpbmVlcmluZywgQUkgYW5kIHN0YXJ0dXBzLiBZb3Ugd29yayBhcyBh + IGZyZWVsYW5jZXIgYW5kIGFyZSBub3cgd29ya2luZyBvbiB3cml0aW5nIGNvbnRlbnQgZm9yIGEg + bmV3IGN1c3RvbWVyLiIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyNSwgIm1heF9y + cG0iOiBudWxsLCAiaTE4biI6IG51bGwsICJsbG0iOiAie1wibmFtZVwiOiBudWxsLCBcIm1vZGVs + X25hbWVcIjogXCJncHQtNG9cIiwgXCJ0ZW1wZXJhdHVyZVwiOiAwLjcsIFwiY2xhc3NcIjogXCJD + aGF0T3BlbkFJXCJ9IiwgImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgInRvb2xzX25hbWVz + IjogW119XUq/BAoKY3Jld190YXNrcxKwBAqtBFt7ImtleSI6ICI1ZmE2NWMwNmE5ZTMxZjJjNjk1 + NDMyNjY4YWNkNjJkZCIsICJpZCI6ICJjNTFlMzZjZC1iMzc3LTRiMjMtOTY1ZC0xMjE4ZjAxN2Rl + NTkiLCAiZGVzY3JpcHRpb24iOiAiQ29tZSB1cCB3aXRoIGEgbGlzdCBvZiA1IGludGVyZXN0aW5n + IGlkZWFzIHRvIGV4cGxvcmUgZm9yIGFuIGFydGljbGUsIHRoZW4gd3JpdGUgb25lIGFtYXppbmcg + cGFyYWdyYXBoIGhpZ2hsaWdodCBmb3IgZWFjaCBpZGVhIHRoYXQgc2hvd2Nhc2VzIGhvdyBnb29k + IGFuIGFydGljbGUgYWJvdXQgdGhpcyB0b3BpYyBjb3VsZCBiZS4gUmV0dXJuIHRoZSBsaXN0IG9m + IGlkZWFzIHdpdGggdGhlaXIgcGFyYWdyYXBoIGFuZCB5b3VyIG5vdGVzLiIsICJleHBlY3RlZF9v + dXRwdXQiOiAiNSBidWxsZXQgcG9pbnRzIHdpdGggYSBwYXJhZ3JhcGggZm9yIGVhY2ggaWRlYS4i + LCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2Vu + dF9yb2xlIjogIk5vbmUiLCAiYWdlbnRfa2V5IjogbnVsbCwgImNvbnRleHQiOiBudWxsLCAidG9v + bHNfbmFtZXMiOiBbXX1dSioKCHBsYXRmb3JtEh4KHG1hY09TLTE0LjEuMS1hcm02NC1hcm0tNjRi + aXRKHAoQcGxhdGZvcm1fcmVsZWFzZRIICgYyMy4xLjBKGwoPcGxhdGZvcm1fc3lzdGVtEggKBkRh + cndpbkp7ChBwbGF0Zm9ybV92ZXJzaW9uEmcKZURhcndpbiBLZXJuZWwgVmVyc2lvbiAyMy4xLjA6 + IE1vbiBPY3QgIDkgMjE6Mjc6MjQgUERUIDIwMjM7IHJvb3Q6eG51LTEwMDAyLjQxLjl+Ni9SRUxF + QVNFX0FSTTY0X1Q2MDAwSgoKBGNwdXMSAhgKegIYAYUBAAEAABLJEgoQC4ERVCPi0URQcsRKT+/y + GxIInSinsu8yhJsqDENyZXcgQ3JlYXRlZDABOcAcwM61xeIXQaiRws61xeIXShoKDmNyZXdhaV92 + ZXJzaW9uEggKBjAuMzYuMEoaCg5weXRob25fdmVyc2lvbhIICgYzLjExLjVKLgoIY3Jld19rZXkS + IgogZTNmZGEwZjMxMTBmZTgwYjE4OTQ3YzAxNDcxNDMwYTRKMQoHY3Jld19pZBImCiQxMTUxYTA2 + Zi02YjdhLTQzZGYtOWZjNi05MzJmYTYzY2RhZjRKHgoMY3Jld19wcm9jZXNzEg4KDGhpZXJhcmNo + aWNhbEoRCgtjcmV3X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2ZfdGFza3MSAhgBShsKFWNy + ZXdfbnVtYmVyX29mX2FnZW50cxICGAJKxQkKC2NyZXdfYWdlbnRzErUJCrIJW3sia2V5IjogIjhi + ZDIxMzliNTk3NTE4MTUwNmU0MWZkOWM0NTYzZDc1IiwgImlkIjogIjc4YmM1N2I4LWIwZTgtNDZl + Ny04OTIzLTQ2ZDJlMWQ4MjQwNyIsICJyb2xlIjogIlJlc2VhcmNoZXIiLCAiZ29hbCI6ICJNYWtl + IHRoZSBiZXN0IHJlc2VhcmNoIGFuZCBhbmFseXNpcyBvbiBjb250ZW50IGFib3V0IEFJIGFuZCBB + SSBhZ2VudHMiLCAiYmFja3N0b3J5IjogIllvdSdyZSBhbiBleHBlcnQgcmVzZWFyY2hlciwgc3Bl + Y2lhbGl6ZWQgaW4gdGVjaG5vbG9neSwgc29mdHdhcmUgZW5naW5lZXJpbmcsIEFJIGFuZCBzdGFy + dHVwcy4gWW91IHdvcmsgYXMgYSBmcmVlbGFuY2VyIGFuZCBpcyBub3cgd29ya2luZyBvbiBkb2lu + ZyByZXNlYXJjaCBhbmQgYW5hbHlzaXMgZm9yIGEgbmV3IGN1c3RvbWVyLiIsICJ2ZXJib3NlPyI6 + IGZhbHNlLCAibWF4X2l0ZXIiOiAyNSwgIm1heF9ycG0iOiBudWxsLCAiaTE4biI6IG51bGwsICJs + bG0iOiAie1wibmFtZVwiOiBudWxsLCBcIm1vZGVsX25hbWVcIjogXCJncHQtNG9cIiwgXCJ0ZW1w + ZXJhdHVyZVwiOiAwLjcsIFwiY2xhc3NcIjogXCJDaGF0T3BlbkFJXCJ9IiwgImRlbGVnYXRpb25f + ZW5hYmxlZD8iOiBmYWxzZSwgInRvb2xzX25hbWVzIjogW119LCB7ImtleSI6ICI5YTUwMTVlZjQ4 + OTVkYzYyNzhkNTQ4MThiYTQ0NmFmNyIsICJpZCI6ICI5NmMyMTk5NS1hZWQyLTRiYzQtYmI2OC03 + YjkwN2U2M2I0MjUiLCAicm9sZSI6ICJTZW5pb3IgV3JpdGVyIiwgImdvYWwiOiAiV3JpdGUgdGhl + IGJlc3QgY29udGVudCBhYm91dCBBSSBhbmQgQUkgYWdlbnRzLiIsICJiYWNrc3RvcnkiOiAiWW91 + J3JlIGEgc2VuaW9yIHdyaXRlciwgc3BlY2lhbGl6ZWQgaW4gdGVjaG5vbG9neSwgc29mdHdhcmUg + ZW5naW5lZXJpbmcsIEFJIGFuZCBzdGFydHVwcy4gWW91IHdvcmsgYXMgYSBmcmVlbGFuY2VyIGFu + ZCBhcmUgbm93IHdvcmtpbmcgb24gd3JpdGluZyBjb250ZW50IGZvciBhIG5ldyBjdXN0b21lci4i + LCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjUsICJtYXhfcnBtIjogbnVsbCwgImkx + OG4iOiBudWxsLCAibGxtIjogIntcIm5hbWVcIjogbnVsbCwgXCJtb2RlbF9uYW1lXCI6IFwiZ3B0 + LTRvXCIsIFwidGVtcGVyYXR1cmVcIjogMC43LCBcImNsYXNzXCI6IFwiQ2hhdE9wZW5BSVwifSIs + ICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJ0b29sc19uYW1lcyI6IFtdfV1KvwQKCmNy + ZXdfdGFza3MSsAQKrQRbeyJrZXkiOiAiNWZhNjVjMDZhOWUzMWYyYzY5NTQzMjY2OGFjZDYyZGQi + LCAiaWQiOiAiMDIzOGY1NWUtZTMwYy00MDIwLWE0ZmQtNWY2ZmUzNjE1NjA4IiwgImRlc2NyaXB0 + aW9uIjogIkNvbWUgdXAgd2l0aCBhIGxpc3Qgb2YgNSBpbnRlcmVzdGluZyBpZGVhcyB0byBleHBs + b3JlIGZvciBhbiBhcnRpY2xlLCB0aGVuIHdyaXRlIG9uZSBhbWF6aW5nIHBhcmFncmFwaCBoaWdo + bGlnaHQgZm9yIGVhY2ggaWRlYSB0aGF0IHNob3djYXNlcyBob3cgZ29vZCBhbiBhcnRpY2xlIGFi + b3V0IHRoaXMgdG9waWMgY291bGQgYmUuIFJldHVybiB0aGUgbGlzdCBvZiBpZGVhcyB3aXRoIHRo + ZWlyIHBhcmFncmFwaCBhbmQgeW91ciBub3Rlcy4iLCAiZXhwZWN0ZWRfb3V0cHV0IjogIjUgYnVs + bGV0IHBvaW50cyB3aXRoIGEgcGFyYWdyYXBoIGZvciBlYWNoIGlkZWEuIiwgImFzeW5jX2V4ZWN1 + dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJOb25l + IiwgImFnZW50X2tleSI6IG51bGwsICJjb250ZXh0IjogbnVsbCwgInRvb2xzX25hbWVzIjogW119 + XUoqCghwbGF0Zm9ybRIeChxtYWNPUy0xNC4xLjEtYXJtNjQtYXJtLTY0Yml0ShwKEHBsYXRmb3Jt + X3JlbGVhc2USCAoGMjMuMS4wShsKD3BsYXRmb3JtX3N5c3RlbRIICgZEYXJ3aW5KewoQcGxhdGZv + cm1fdmVyc2lvbhJnCmVEYXJ3aW4gS2VybmVsIFZlcnNpb24gMjMuMS4wOiBNb24gT2N0ICA5IDIx + OjI3OjI0IFBEVCAyMDIzOyByb290OnhudS0xMDAwMi40MS45fjYvUkVMRUFTRV9BUk02NF9UNjAw + MEoKCgRjcHVzEgIYCnoCGAGFAQABAAASwRUKEK+2FWN+LUsj3/10yDBx5MMSCPaor0csAVI6KgxD + cmV3IENyZWF0ZWQwATlwEcXPtcXiF0FQscfPtcXiF0oaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjM2 + LjBKGgoOcHl0aG9uX3ZlcnNpb24SCAoGMy4xMS41Si4KCGNyZXdfa2V5EiIKIGQzODQ2YzlkMjc2 + ZThlNmU0M2UzMWY2MTc2MzU3YjRmSjEKB2NyZXdfaWQSJgokYzE0MGJlNTgtOTJjZS00YzQyLWJk + YjQtOThjNTlmMGUzYzAxShwKDGNyZXdfcHJvY2VzcxIMCgpzZXF1ZW50aWFsShEKC2NyZXdfbWVt + b3J5EgIQAEoaChRjcmV3X251bWJlcl9vZl90YXNrcxICGAJKGwoVY3Jld19udW1iZXJfb2ZfYWdl + bnRzEgIYAkrFCQoLY3Jld19hZ2VudHMStQkKsglbeyJrZXkiOiAiOGJkMjEzOWI1OTc1MTgxNTA2 + ZTQxZmQ5YzQ1NjNkNzUiLCAiaWQiOiAiNzhiYzU3YjgtYjBlOC00NmU3LTg5MjMtNDZkMmUxZDgy + NDA3IiwgInJvbGUiOiAiUmVzZWFyY2hlciIsICJnb2FsIjogIk1ha2UgdGhlIGJlc3QgcmVzZWFy + Y2ggYW5kIGFuYWx5c2lzIG9uIGNvbnRlbnQgYWJvdXQgQUkgYW5kIEFJIGFnZW50cyIsICJiYWNr + c3RvcnkiOiAiWW91J3JlIGFuIGV4cGVydCByZXNlYXJjaGVyLCBzcGVjaWFsaXplZCBpbiB0ZWNo + bm9sb2d5LCBzb2Z0d2FyZSBlbmdpbmVlcmluZywgQUkgYW5kIHN0YXJ0dXBzLiBZb3Ugd29yayBh + cyBhIGZyZWVsYW5jZXIgYW5kIGlzIG5vdyB3b3JraW5nIG9uIGRvaW5nIHJlc2VhcmNoIGFuZCBh + bmFseXNpcyBmb3IgYSBuZXcgY3VzdG9tZXIuIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRl + ciI6IDI1LCAibWF4X3JwbSI6IG51bGwsICJpMThuIjogbnVsbCwgImxsbSI6ICJ7XCJuYW1lXCI6 + IG51bGwsIFwibW9kZWxfbmFtZVwiOiBcImdwdC00b1wiLCBcInRlbXBlcmF0dXJlXCI6IDAuNywg + XCJjbGFzc1wiOiBcIkNoYXRPcGVuQUlcIn0iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNl + LCAidG9vbHNfbmFtZXMiOiBbXX0sIHsia2V5IjogIjlhNTAxNWVmNDg5NWRjNjI3OGQ1NDgxOGJh + NDQ2YWY3IiwgImlkIjogIjk2YzIxOTk1LWFlZDItNGJjNC1iYjY4LTdiOTA3ZTYzYjQyNSIsICJy + b2xlIjogIlNlbmlvciBXcml0ZXIiLCAiZ29hbCI6ICJXcml0ZSB0aGUgYmVzdCBjb250ZW50IGFi + b3V0IEFJIGFuZCBBSSBhZ2VudHMuIiwgImJhY2tzdG9yeSI6ICJZb3UncmUgYSBzZW5pb3Igd3Jp + dGVyLCBzcGVjaWFsaXplZCBpbiB0ZWNobm9sb2d5LCBzb2Z0d2FyZSBlbmdpbmVlcmluZywgQUkg + YW5kIHN0YXJ0dXBzLiBZb3Ugd29yayBhcyBhIGZyZWVsYW5jZXIgYW5kIGFyZSBub3cgd29ya2lu + ZyBvbiB3cml0aW5nIGNvbnRlbnQgZm9yIGEgbmV3IGN1c3RvbWVyLiIsICJ2ZXJib3NlPyI6IGZh + bHNlLCAibWF4X2l0ZXIiOiAyNSwgIm1heF9ycG0iOiBudWxsLCAiaTE4biI6IG51bGwsICJsbG0i + OiAie1wibmFtZVwiOiBudWxsLCBcIm1vZGVsX25hbWVcIjogXCJncHQtNG9cIiwgXCJ0ZW1wZXJh + dHVyZVwiOiAwLjcsIFwiY2xhc3NcIjogXCJDaGF0T3BlbkFJXCJ9IiwgImRlbGVnYXRpb25fZW5h + YmxlZD8iOiBmYWxzZSwgInRvb2xzX25hbWVzIjogW119XUq5BwoKY3Jld190YXNrcxKqBwqnB1t7 + ImtleSI6ICJlOWU2YjcyYWFjMzI2NDU5ZGQ3MDY4ZjBiMTcxN2MxYyIsICJpZCI6ICI5NWE1NzQz + Yy1lMjRmLTQ4ODItYmQ3Mi02ZmZlZjViYTMzNTYiLCAiZGVzY3JpcHRpb24iOiAiR2VuZXJhdGUg + YSBsaXN0IG9mIDUgaW50ZXJlc3RpbmcgaWRlYXMgdG8gZXhwbG9yZSBmb3IgYW4gYXJ0aWNsZSwg + d2hlcmUgZWFjaCBidWxsZXRwb2ludCBpcyB1bmRlciAxNSB3b3Jkcy4iLCAiZXhwZWN0ZWRfb3V0 + cHV0IjogIkJ1bGxldCBwb2ludCBsaXN0IG9mIDUgaW1wb3J0YW50IGV2ZW50cy4gTm8gYWRkaXRp + b25hbCBjb21tZW50YXJ5LiIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1 + dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiUmVzZWFyY2hlciIsICJhZ2VudF9rZXkiOiAiOGJk + MjEzOWI1OTc1MTgxNTA2ZTQxZmQ5YzQ1NjNkNzUiLCAiY29udGV4dCI6IG51bGwsICJ0b29sc19u + YW1lcyI6IFtdfSwgeyJrZXkiOiAiZWVlZTdlNzNkNWRmNjZkNDhkMmQ4MDdiYWZmODc0ZjMiLCAi + aWQiOiAiNzA4MGY4M2YtNmQ1Zi00MzM0LTgwZTctODI3NzEzNmQ4ZDdkIiwgImRlc2NyaXB0aW9u + IjogIldyaXRlIGEgc2VudGVuY2UgYWJvdXQgdGhlIGV2ZW50cyIsICJleHBlY3RlZF9vdXRwdXQi + OiAiQSBzZW50ZW5jZSBhYm91dCB0aGUgZXZlbnRzIiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxz + ZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJTZW5pb3IgV3JpdGVyIiwg + ImFnZW50X2tleSI6ICI5YTUwMTVlZjQ4OTVkYzYyNzhkNTQ4MThiYTQ0NmFmNyIsICJjb250ZXh0 + IjogWyJHZW5lcmF0ZSBhIGxpc3Qgb2YgNSBpbnRlcmVzdGluZyBpZGVhcyB0byBleHBsb3JlIGZv + ciBhbiBhcnRpY2xlLCB3aGVyZSBlYWNoIGJ1bGxldHBvaW50IGlzIHVuZGVyIDE1IHdvcmRzLiJd + LCAidG9vbHNfbmFtZXMiOiBbXX1dSioKCHBsYXRmb3JtEh4KHG1hY09TLTE0LjEuMS1hcm02NC1h + cm0tNjRiaXRKHAoQcGxhdGZvcm1fcmVsZWFzZRIICgYyMy4xLjBKGwoPcGxhdGZvcm1fc3lzdGVt + EggKBkRhcndpbkp7ChBwbGF0Zm9ybV92ZXJzaW9uEmcKZURhcndpbiBLZXJuZWwgVmVyc2lvbiAy + My4xLjA6IE1vbiBPY3QgIDkgMjE6Mjc6MjQgUERUIDIwMjM7IHJvb3Q6eG51LTEwMDAyLjQxLjl+ + Ni9SRUxFQVNFX0FSTTY0X1Q2MDAwSgoKBGNwdXMSAhgKegIYAYUBAAEAABKxCwoQrufF6FYNBLX1 + X9l1YHzEsBIIg+LU+V/XPr0qDENyZXcgQ3JlYXRlZDABOYhBV9K1xeIXQcB/WdK1xeIXShoKDmNy + ZXdhaV92ZXJzaW9uEggKBjAuMzYuMEoaCg5weXRob25fdmVyc2lvbhIICgYzLjExLjVKLgoIY3Jl + d19rZXkSIgogNjczOGFkNWI4Y2IzZTZmMWMxYzkzNTBiOTZjMmU2NzhKMQoHY3Jld19pZBImCiQw + ZDFjYjk0Yy1kMjgyLTQwYWUtYWVlYy0yN2JjNzc1NGZmOThKHAoMY3Jld19wcm9jZXNzEgwKCnNl + cXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdfbnVtYmVyX29mX3Rhc2tzEgIYAUob + ChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgBSuEDCgtjcmV3X2FnZW50cxLRAwrOA1t7ImtleSI6 + ICI1MTJhNmRjMzc5ZjY2YjIxZWVhYjI0ZTYzNDgzNmY3MiIsICJpZCI6ICJjOTMwNzU2OS05OTRl + LTRkMTItYWI5OS01NzlhYjZhNDMyMDkiLCAicm9sZSI6ICJDb250ZW50IFdyaXRlciIsICJnb2Fs + IjogIldyaXRlIGVuZ2FnaW5nIGNvbnRlbnQgb24gdmFyaW91cyB0b3BpY3MuIiwgImJhY2tzdG9y + eSI6ICJZb3UgaGF2ZSBhIGJhY2tncm91bmQgaW4gam91cm5hbGlzbSBhbmQgY3JlYXRpdmUgd3Jp + dGluZy4iLCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjUsICJtYXhfcnBtIjogbnVs + bCwgImkxOG4iOiBudWxsLCAibGxtIjogIntcIm5hbWVcIjogbnVsbCwgXCJtb2RlbF9uYW1lXCI6 + IFwiZ3B0LTRvXCIsIFwidGVtcGVyYXR1cmVcIjogMC43LCBcImNsYXNzXCI6IFwiQ2hhdE9wZW5B + SVwifSIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogdHJ1ZSwgInRvb2xzX25hbWVzIjogW119XUqN + AwoKY3Jld190YXNrcxL+Agr7Alt7ImtleSI6ICIzNDc3MDc2YmUzYWY3MTMwNDYyZWRhYTJlYjhh + MDQ4ZSIsICJpZCI6ICI4NDMxNmNhOS05ZDNmLTRkZjctODQ4OC0zMDBmNDMxYWE1MDgiLCAiZGVz + Y3JpcHRpb24iOiAiV3JpdGUgYSBkZXRhaWxlZCBhcnRpY2xlIGFib3V0IEFJIGluIGhlYWx0aGNh + cmUuIiwgImV4cGVjdGVkX291dHB1dCI6ICJBIDEgcGFyYWdyYXBoIGFydGljbGUgYWJvdXQgQUku + IiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdl + bnRfcm9sZSI6ICJDb250ZW50IFdyaXRlciIsICJhZ2VudF9rZXkiOiAiNTEyYTZkYzM3OWY2NmIy + MWVlYWIyNGU2MzQ4MzZmNzIiLCAiY29udGV4dCI6IG51bGwsICJ0b29sc19uYW1lcyI6IFtdfV1K + KgoIcGxhdGZvcm0SHgocbWFjT1MtMTQuMS4xLWFybTY0LWFybS02NGJpdEocChBwbGF0Zm9ybV9y + ZWxlYXNlEggKBjIzLjEuMEobCg9wbGF0Zm9ybV9zeXN0ZW0SCAoGRGFyd2luSnsKEHBsYXRmb3Jt + X3ZlcnNpb24SZwplRGFyd2luIEtlcm5lbCBWZXJzaW9uIDIzLjEuMDogTW9uIE9jdCAgOSAyMToy + NzoyNCBQRFQgMjAyMzsgcm9vdDp4bnUtMTAwMDIuNDEuOX42L1JFTEVBU0VfQVJNNjRfVDYwMDBK + CgoEY3B1cxICGAp6AhgBhQEAAQAA + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, br + Connection: + - keep-alive + Content-Length: + - '23733' + Content-Type: + - application/x-protobuf + User-Agent: + - OTel-OTLP-Exporter-Python/1.25.0 + method: POST + uri: https://telemetry.crewai.com:4319/v1/traces + response: + body: + string: "\n\0" + headers: + Content-Length: + - '2' + Content-Type: + - application/x-protobuf + Date: + - Tue, 16 Jul 2024 18:43:12 GMT + status: + code: 200 + message: OK +version: 1 diff --git a/tests/crew_test.py b/tests/crew_test.py index b9f38789e..bb1a7b5bd 100644 --- a/tests/crew_test.py +++ b/tests/crew_test.py @@ -4,13 +4,14 @@ import hashlib import json from concurrent.futures import Future from unittest import mock -from unittest.mock import patch +from unittest.mock import MagicMock, patch import pydantic_core import pytest from crewai.agent import Agent from crewai.agents.cache import CacheHandler +from crewai.conditional_task import ConditionalTask from crewai.crew import Crew from crewai.crews.crew_output import CrewOutput from crewai.memory.contextual.contextual_memory import ContextualMemory @@ -2260,3 +2261,93 @@ def test_key(): ).hexdigest() assert crew.key == hash + + +def test_conditional_task_requirement_breaks_when_singular_conditional_task(): + task = ConditionalTask( + description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", + expected_output="5 bullet points with a paragraph for each idea.", + ) + + with pytest.raises(pydantic_core._pydantic_core.ValidationError): + Crew( + agents=[researcher, writer], + tasks=[task], + ) + + +def test_conditional_task_requirement_breaks_when_task_async(): + task = ConditionalTask( + description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", + expected_output="5 bullet points with a paragraph for each idea.", + execute_async=True, + ) + + with pytest.raises(pydantic_core._pydantic_core.ValidationError): + Crew( + agents=[researcher, writer], + tasks=[task], + ) + + +@pytest.mark.vcr(filter_headers=["authorization"]) +def test_conditional_should_skip(): + task1 = Task(description="Return hello", expected_output="say hi", agent=researcher) + + condition_mock = MagicMock(return_value=False) + task2 = ConditionalTask( + description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", + expected_output="5 bullet points with a paragraph for each idea.", + condition=condition_mock, + agent=writer, + ) + crew_met = Crew( + agents=[researcher, writer], + tasks=[task1, task2], + ) + with patch.object(Task, "execute_sync") as mock_execute_sync: + mock_execute_sync.return_value = TaskOutput( + description="Task 1 description", + raw="Task 1 output", + agent="Researcher", + ) + + result = crew_met.kickoff() + assert mock_execute_sync.call_count == 1 + + assert condition_mock.call_count == 1 + assert condition_mock() is False + + assert task2.output is None + assert result.raw.startswith("Task 1 output") + + +@pytest.mark.vcr(filter_headers=["authorization"]) +def test_conditional_should_execute(): + task1 = Task(description="Return hello", expected_output="say hi", agent=researcher) + + condition_mock = MagicMock( + return_value=True + ) # should execute this conditional task + task2 = ConditionalTask( + description="Come up with a list of 5 interesting ideas to explore for an article, then write one amazing paragraph highlight for each idea that showcases how good an article about this topic could be. Return the list of ideas with their paragraph and your notes.", + expected_output="5 bullet points with a paragraph for each idea.", + condition=condition_mock, + agent=writer, + ) + crew_met = Crew( + agents=[researcher, writer], + tasks=[task1, task2], + ) + with patch.object(Task, "execute_sync") as mock_execute_sync: + mock_execute_sync.return_value = TaskOutput( + description="Task 1 description", + raw="Task 1 output", + agent="Researcher", + ) + + crew_met.kickoff() + + assert condition_mock.call_count == 1 + assert condition_mock() is True + assert mock_execute_sync.call_count == 2