mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Define Workflow and Executor APIs (#272)
* Workflow init commit * Add samples and clean up * ExecutionContext -> WorkflowContext * Address comments 1 * Fix mypy * flatting folder structure, and rename contexts * Remove add_loop * Add map reduce sample, remove Activation conditions * Add AgentExecutor and allow multiple handlers per executor * Minor improvement * Add RequestInfoExecutor * Add unit tests part 1 * Address comments 2 * Pre-commit update * Add run method and more unit tests * Add xml docs * run_stream -> run_streaming * message_handler -> handler --------- Co-authored-by: Chris <66376200+crickman@users.noreply.github.com> Co-authored-by: Evan Mattson <evan.mattson@microsoft.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
0d4d7abde1
commit
c8694a8c76
@@ -0,0 +1,199 @@
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
|
||||
Lorem ipsum dolor sit amet consectetur adipiscing elit. Quisque faucibus ex sapien vitae pellentesque sem placerat. In id cursus mi pretium tellus duis convallis. Tempus leo eu aenean sed diam urna tempor. Pulvinar vivamus fringilla lacus nec metus bibendum egestas. Iaculis massa nisl malesuada lacinia integer nunc posuere. Ut hendrerit semper vel class aptent taciti sociosqu. Ad litora torquent per conubia nostra inceptos himenaeos.
|
||||
@@ -0,0 +1,65 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.workflow import Executor, WorkflowBuilder, WorkflowCompletedEvent, WorkflowContext, handler
|
||||
|
||||
"""
|
||||
The following sample demonstrates a basic workflow with two executors
|
||||
that process a string in sequence. The first executor converts the
|
||||
input string to uppercase, and the second executor reverses the string.
|
||||
"""
|
||||
|
||||
|
||||
class UpperCaseExecutor(Executor):
|
||||
"""An executor that converts text to uppercase."""
|
||||
|
||||
@handler(output_types=[str])
|
||||
async def to_upper_case(self, text: str, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by converting the input string to uppercase."""
|
||||
result = text.upper()
|
||||
|
||||
# Send the result to the next executor in the workflow.
|
||||
await ctx.send_message(result)
|
||||
|
||||
|
||||
class ReverseTextExecutor(Executor):
|
||||
"""An executor that reverses text."""
|
||||
|
||||
@handler
|
||||
async def reverse_text(self, text: str, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by reversing the input string."""
|
||||
result = text[::-1]
|
||||
|
||||
# Send the result with a workflow completion event.
|
||||
await ctx.add_event(WorkflowCompletedEvent(result))
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to run the workflow."""
|
||||
# Step 1: Create the executors.
|
||||
upper_case_executor = UpperCaseExecutor(id="upper_case_executor")
|
||||
reverse_text_executor = ReverseTextExecutor(id="reverse_text_executor")
|
||||
|
||||
# Step 2: Build the workflow with the defined edges.
|
||||
workflow = (
|
||||
WorkflowBuilder()
|
||||
.add_edge(upper_case_executor, reverse_text_executor)
|
||||
.set_start_executor(upper_case_executor)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Step 3: Run the workflow with an initial message.
|
||||
completion_event = None
|
||||
async for event in workflow.run_streaming("hello world"):
|
||||
print(f"Event: {event}")
|
||||
if isinstance(event, WorkflowCompletedEvent):
|
||||
# The WorkflowCompletedEvent contains the final result.
|
||||
completion_event = event
|
||||
|
||||
if completion_event:
|
||||
print(f"Workflow completed with result: {completion_event.data}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.workflow import Executor, WorkflowBuilder, WorkflowCompletedEvent, WorkflowContext, handler
|
||||
|
||||
"""
|
||||
The following sample demonstrates a basic workflow with two executors
|
||||
that process a string in sequence. The first executor converts the
|
||||
input string to uppercase, and the second executor reverses the string.
|
||||
"""
|
||||
|
||||
|
||||
class UpperCaseExecutor(Executor):
|
||||
"""An executor that converts text to uppercase."""
|
||||
|
||||
@handler(output_types=[str])
|
||||
async def to_upper_case(self, text: str, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by converting the input string to uppercase."""
|
||||
result = text.upper()
|
||||
|
||||
# Send the result to the next executor in the workflow.
|
||||
await ctx.send_message(result)
|
||||
|
||||
|
||||
class ReverseTextExecutor(Executor):
|
||||
"""An executor that reverses text."""
|
||||
|
||||
@handler
|
||||
async def reverse_text(self, text: str, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by reversing the input string."""
|
||||
result = text[::-1]
|
||||
|
||||
# Send the result with a workflow completion event.
|
||||
await ctx.add_event(WorkflowCompletedEvent(result))
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to run the workflow."""
|
||||
# Step 1: Create the executors.
|
||||
upper_case_executor = UpperCaseExecutor(id="upper_case_executor")
|
||||
reverse_text_executor = ReverseTextExecutor(id="reverse_text_executor")
|
||||
|
||||
# Step 2: Build the workflow with the defined edges.
|
||||
workflow = (
|
||||
WorkflowBuilder()
|
||||
.add_edge(upper_case_executor, reverse_text_executor)
|
||||
.set_start_executor(upper_case_executor)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Step 3: Run the workflow with an initial message.
|
||||
events = await workflow.run("hello world")
|
||||
print(events.get_completed_event())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,119 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from dataclasses import dataclass
|
||||
|
||||
from agent_framework.workflow import (
|
||||
Executor,
|
||||
WorkflowBuilder,
|
||||
WorkflowCompletedEvent,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
|
||||
"""
|
||||
The following sample demonstrates a basic workflow with two executors
|
||||
that detect spam messages and respond accordingly. The first executor
|
||||
checks if the input string is spam, and depending on the result, the
|
||||
workflow takes different paths.
|
||||
"""
|
||||
|
||||
|
||||
@dataclass
|
||||
class SpamDetectorResponse:
|
||||
"""A data class to hold the email message content."""
|
||||
|
||||
email: str
|
||||
is_spam: bool = False
|
||||
|
||||
|
||||
class SpamDetector(Executor):
|
||||
"""An executor that determines if a message is spam."""
|
||||
|
||||
def __init__(self, spam_keywords: list[str], id: str | None = None):
|
||||
"""Initialize the executor with spam keywords."""
|
||||
super().__init__(id=id)
|
||||
self._spam_keywords = spam_keywords
|
||||
|
||||
@handler(output_types=[SpamDetectorResponse])
|
||||
async def handle_email(self, email: str, ctx: WorkflowContext) -> None:
|
||||
"""Determine if the input string is spam."""
|
||||
result = any(keyword in email.lower() for keyword in self._spam_keywords)
|
||||
|
||||
await ctx.send_message(SpamDetectorResponse(email=email, is_spam=result))
|
||||
|
||||
|
||||
class SendResponse(Executor):
|
||||
"""An executor that responds to a message based on spam detection."""
|
||||
|
||||
@handler
|
||||
async def handle_detector_response(
|
||||
self,
|
||||
spam_detector_response: SpamDetectorResponse,
|
||||
ctx: WorkflowContext,
|
||||
) -> None:
|
||||
"""Respond with a message based on whether the input is spam."""
|
||||
if spam_detector_response.is_spam:
|
||||
raise RuntimeError("Input is spam, cannot respond.")
|
||||
|
||||
# Simulate processing delay
|
||||
print(f"Responding to message: {spam_detector_response.email}")
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await ctx.add_event(WorkflowCompletedEvent("Message processed successfully."))
|
||||
|
||||
|
||||
class RemoveSpam(Executor):
|
||||
"""An executor that removes spam messages."""
|
||||
|
||||
@handler
|
||||
async def handle_detector_response(
|
||||
self,
|
||||
spam_detector_response: SpamDetectorResponse,
|
||||
ctx: WorkflowContext,
|
||||
) -> None:
|
||||
"""Remove the spam message."""
|
||||
if spam_detector_response.is_spam is False:
|
||||
raise RuntimeError("Input is not spam, cannot remove.")
|
||||
|
||||
# Simulate processing delay
|
||||
print(f"Removing spam message: {spam_detector_response.email}")
|
||||
await asyncio.sleep(1)
|
||||
|
||||
await ctx.add_event(WorkflowCompletedEvent("Spam message removed."))
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to run the workflow."""
|
||||
# Keyword based spam detection
|
||||
spam_keywords = ["spam", "advertisement", "offer"]
|
||||
|
||||
# Step 1: Create the executors.
|
||||
spam_detector = SpamDetector(spam_keywords, id="spam_detector")
|
||||
send_response = SendResponse(id="send_response")
|
||||
remove_spam = RemoveSpam(id="remove_spam")
|
||||
|
||||
# Step 2: Build the workflow with the defined edges with conditions.
|
||||
workflow = (
|
||||
WorkflowBuilder()
|
||||
.set_start_executor(spam_detector)
|
||||
.add_edge(
|
||||
spam_detector,
|
||||
send_response,
|
||||
condition=lambda x: x.is_spam is False,
|
||||
)
|
||||
.add_edge(
|
||||
spam_detector,
|
||||
remove_spam,
|
||||
condition=lambda x: x.is_spam is True,
|
||||
)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Step 3: Run the workflow with an input message.
|
||||
async for event in workflow.run_streaming("This is a spam."):
|
||||
print(f"Event: {event}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,120 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from enum import Enum
|
||||
|
||||
from agent_framework.workflow import (
|
||||
Executor,
|
||||
ExecutorCompletedEvent,
|
||||
WorkflowBuilder,
|
||||
WorkflowCompletedEvent,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
|
||||
"""
|
||||
The following sample demonstrates a basic workflow with two executors
|
||||
where one executor guesses a number and the other executor judges the
|
||||
guess iteratively.
|
||||
"""
|
||||
|
||||
|
||||
class NumberSignal(Enum):
|
||||
"""Enum to represent number signals for the workflow."""
|
||||
|
||||
# The target number is above the guess.
|
||||
ABOVE = "above"
|
||||
# The target number is below the guess.
|
||||
BELOW = "below"
|
||||
# The guess matches the target number.
|
||||
MATCHED = "matched"
|
||||
# Initial signal to start the guessing process.
|
||||
INIT = "init"
|
||||
|
||||
|
||||
class GuessNumberExecutor(Executor):
|
||||
"""An executor that guesses a number."""
|
||||
|
||||
def __init__(self, bound: tuple[int, int], id: str | None = None):
|
||||
"""Initialize the executor with a target number."""
|
||||
super().__init__(id=id)
|
||||
self._lower = bound[0]
|
||||
self._upper = bound[1]
|
||||
|
||||
@handler(output_types=[int])
|
||||
async def guess_number(self, feedback: NumberSignal, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by guessing a number."""
|
||||
if feedback == NumberSignal.INIT:
|
||||
self._guess = (self._lower + self._upper) // 2
|
||||
await ctx.send_message(self._guess)
|
||||
elif feedback == NumberSignal.MATCHED:
|
||||
# The previous guess was correct.
|
||||
await ctx.add_event(WorkflowCompletedEvent(f"Guessed the number: {self._guess}"))
|
||||
elif feedback == NumberSignal.ABOVE:
|
||||
# The previous guess was too low.
|
||||
# Update the lower bound to the previous guess.
|
||||
# Generate a new number that is between the new bounds.
|
||||
self._lower = self._guess + 1
|
||||
self._guess = (self._lower + self._upper) // 2
|
||||
await ctx.send_message(self._guess)
|
||||
else:
|
||||
# The previous guess was too high.
|
||||
# Update the upper bound to the previous guess.
|
||||
# Generate a new number that is between the new bounds.
|
||||
self._upper = self._guess - 1
|
||||
self._guess = (self._lower + self._upper) // 2
|
||||
await ctx.send_message(self._guess)
|
||||
|
||||
|
||||
class JudgeExecutor(Executor):
|
||||
"""An executor that judges the guessed number."""
|
||||
|
||||
def __init__(self, target: int, id: str | None = None):
|
||||
"""Initialize the executor with a target number."""
|
||||
super().__init__(id=id)
|
||||
self._target = target
|
||||
|
||||
@handler(output_types=[NumberSignal])
|
||||
async def judge(self, number: int, ctx: WorkflowContext) -> None:
|
||||
"""Judge the guessed number."""
|
||||
if number == self._target:
|
||||
result = NumberSignal.MATCHED
|
||||
elif number < self._target:
|
||||
result = NumberSignal.ABOVE
|
||||
else:
|
||||
result = NumberSignal.BELOW
|
||||
|
||||
await ctx.send_message(result)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to run the workflow."""
|
||||
# Step 1: Create the executors.
|
||||
guess_number_executor = GuessNumberExecutor((1, 100))
|
||||
judge_executor = JudgeExecutor(30)
|
||||
|
||||
# Step 2: Build the workflow with the defined edges.
|
||||
# This time we are creating a loop in the workflow.
|
||||
workflow = (
|
||||
WorkflowBuilder()
|
||||
.add_edge(guess_number_executor, judge_executor)
|
||||
.add_edge(judge_executor, guess_number_executor)
|
||||
.set_start_executor(guess_number_executor)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Step 3: Run the workflow and print the events.
|
||||
iterations = 0
|
||||
async for event in workflow.run_streaming(NumberSignal.INIT):
|
||||
if isinstance(event, ExecutorCompletedEvent) and event.executor_id == guess_number_executor.id:
|
||||
iterations += 1
|
||||
print(f"Event: {event}")
|
||||
|
||||
# This is essentially a binary search, so the number of iterations should be logarithmic.
|
||||
# The maximum number of iterations is [log2(range size)]. For a range of 1 to 100, this is log2(100) which is 7.
|
||||
# Subtract because the last round is the MATCHED event.
|
||||
print(f"Guessed {iterations - 1} times.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,147 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import ChatClientAgent, ChatMessage, ChatRole
|
||||
from agent_framework.azure import AzureChatClient
|
||||
from agent_framework.workflow import (
|
||||
AgentExecutor,
|
||||
AgentExecutorRequest,
|
||||
AgentExecutorResponse,
|
||||
AgentRunEvent,
|
||||
Executor,
|
||||
WorkflowBuilder,
|
||||
WorkflowCompletedEvent,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
|
||||
"""
|
||||
The following sample demonstrates a basic workflow that simulates
|
||||
a round-robin group chat.
|
||||
"""
|
||||
|
||||
|
||||
class RoundRobinGroupChatManager(Executor):
|
||||
"""An executor that manages a round-robin group chat."""
|
||||
|
||||
def __init__(self, members: list[str], max_round: int, id: str | None = None):
|
||||
"""Initialize the executor with a unique identifier."""
|
||||
super().__init__(id)
|
||||
self._members = members
|
||||
self._max_round = max_round
|
||||
self._current_round = 0
|
||||
|
||||
@handler(output_types=[AgentExecutorRequest])
|
||||
async def start(self, task: str, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by sending messages to the next executor in the round-robin sequence."""
|
||||
initial_message = ChatMessage(ChatRole.USER, text=task)
|
||||
|
||||
# Send the initial message to the members
|
||||
await asyncio.gather(*[
|
||||
ctx.send_message(
|
||||
AgentExecutorRequest(messages=[initial_message], should_respond=False),
|
||||
target_id=member_id,
|
||||
)
|
||||
for member_id in self._members
|
||||
])
|
||||
|
||||
# Invoke the first member to start the round-robin chat
|
||||
await ctx.send_message(
|
||||
AgentExecutorRequest(messages=[], should_respond=True),
|
||||
target_id=self._get_next_member(),
|
||||
)
|
||||
|
||||
@handler(output_types=[AgentExecutorRequest])
|
||||
async def handle_agent_response(self, response: AgentExecutorResponse, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by sending messages to the next executor in the round-robin sequence."""
|
||||
# Send the response to the other members
|
||||
await asyncio.gather(*[
|
||||
ctx.send_message(
|
||||
AgentExecutorRequest(messages=response.agent_run_response.messages, should_respond=False),
|
||||
target_id=member_id,
|
||||
)
|
||||
for member_id in self._members
|
||||
if member_id != response.executor_id
|
||||
])
|
||||
|
||||
# Check for termination condition
|
||||
if self._should_terminate():
|
||||
await ctx.add_event(WorkflowCompletedEvent(data=response))
|
||||
return
|
||||
|
||||
# Request the next member to respond
|
||||
selection = self._get_next_member()
|
||||
await ctx.send_message(AgentExecutorRequest(messages=[], should_respond=True), target_id=selection)
|
||||
|
||||
def _should_terminate(self) -> bool:
|
||||
"""Determine if the group chat should terminate based on the current round."""
|
||||
return self._current_round >= self._max_round
|
||||
|
||||
def _get_next_member(self) -> str:
|
||||
"""Get the next member in the round-robin sequence."""
|
||||
next_member = self._members[self._current_round % len(self._members)]
|
||||
self._current_round += 1
|
||||
|
||||
return next_member
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to run the group chat workflow."""
|
||||
|
||||
# Step 1: Create the executors.
|
||||
chat_client = AzureChatClient()
|
||||
writer = AgentExecutor(
|
||||
ChatClientAgent(
|
||||
chat_client,
|
||||
instructions=(
|
||||
"You are an excellent content writer. You create new content and edit contents based on the feedback."
|
||||
),
|
||||
),
|
||||
id="writer",
|
||||
)
|
||||
reviewer = AgentExecutor(
|
||||
ChatClientAgent(
|
||||
chat_client,
|
||||
instructions=(
|
||||
"You are an excellent content reviewer. You review the content and provide feedback to the writer."
|
||||
),
|
||||
),
|
||||
id="reviewer",
|
||||
)
|
||||
|
||||
group_chat_manager = RoundRobinGroupChatManager(
|
||||
members=[writer.id, reviewer.id],
|
||||
# max_rounds is odd, so that the writer gets the last round
|
||||
max_round=5,
|
||||
id="group_chat_manager",
|
||||
)
|
||||
|
||||
# Step 2: Build the workflow with the defined edges.
|
||||
workflow = (
|
||||
WorkflowBuilder()
|
||||
.set_start_executor(group_chat_manager)
|
||||
.add_edge(group_chat_manager, writer)
|
||||
.add_edge(group_chat_manager, reviewer)
|
||||
.add_edge(writer, group_chat_manager)
|
||||
.add_edge(reviewer, group_chat_manager)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Step 3: Run the workflow with an initial message.
|
||||
completion_event = None
|
||||
async for event in workflow.run_streaming(
|
||||
"Create a slogan for a new electric SUV that is affordable and fun to drive."
|
||||
):
|
||||
if isinstance(event, AgentRunEvent):
|
||||
print(f"{event}")
|
||||
|
||||
if isinstance(event, WorkflowCompletedEvent):
|
||||
completion_event = event
|
||||
|
||||
if completion_event:
|
||||
print(f"Completion Event: {completion_event}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,215 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework import ChatClientAgent, ChatMessage, ChatRole
|
||||
from agent_framework.azure import AzureChatClient
|
||||
from agent_framework.workflow import (
|
||||
AgentExecutor,
|
||||
AgentExecutorRequest,
|
||||
AgentExecutorResponse,
|
||||
Executor,
|
||||
RequestInfoEvent,
|
||||
RequestInfoExecutor,
|
||||
RequestInfoMessage,
|
||||
WorkflowBuilder,
|
||||
WorkflowCompletedEvent,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
|
||||
"""
|
||||
The following sample demonstrates a basic workflow that simulates
|
||||
a round-robin group chat with a Human-in-the-Loop (HIL) executor.
|
||||
"""
|
||||
|
||||
|
||||
class CriticGroupChatManager(Executor):
|
||||
"""An executor that manages a round-robin group chat."""
|
||||
|
||||
def __init__(self, members: list[str], id: str | None = None):
|
||||
"""Initialize the executor with a unique identifier."""
|
||||
super().__init__(id)
|
||||
self._members = members
|
||||
self._current_round = 0
|
||||
self._chat_history: list[ChatMessage] = []
|
||||
|
||||
@handler(output_types=[AgentExecutorRequest])
|
||||
async def start(self, task: str, ctx: WorkflowContext) -> None:
|
||||
"""Handler that starts the group chat with an initial task."""
|
||||
initial_message = ChatMessage(ChatRole.USER, text=task)
|
||||
|
||||
# Send the initial message to the members
|
||||
await self._broadcast_message([initial_message], ctx)
|
||||
|
||||
# Invoke the first member to start the round-robin chat
|
||||
await ctx.send_message(
|
||||
AgentExecutorRequest(messages=[], should_respond=True),
|
||||
target_id=self._get_next_member(),
|
||||
)
|
||||
|
||||
# Update the cache with the initial message
|
||||
self._chat_history.append(initial_message)
|
||||
|
||||
@handler(output_types=[AgentExecutorRequest, RequestInfoMessage])
|
||||
async def handle_agent_response(self, response: AgentExecutorResponse, ctx: WorkflowContext) -> None:
|
||||
"""Handler that processes the response from the agent."""
|
||||
# Update the chat history with the response
|
||||
self._chat_history.extend(response.agent_run_response.messages)
|
||||
|
||||
# Send the response to the other members
|
||||
await self._broadcast_message(response.agent_run_response.messages, ctx, exclude_id=response.executor_id)
|
||||
|
||||
# Check if we need to request additional information
|
||||
if self._should_request_info():
|
||||
await ctx.send_message(RequestInfoMessage())
|
||||
return
|
||||
|
||||
# Check for termination condition
|
||||
if self._should_terminate():
|
||||
await ctx.add_event(WorkflowCompletedEvent(data=response))
|
||||
return
|
||||
|
||||
# Request the next member to respond
|
||||
selection = self._get_next_member()
|
||||
await ctx.send_message(AgentExecutorRequest(messages=[], should_respond=True), target_id=selection)
|
||||
|
||||
@handler(output_types=[AgentExecutorRequest])
|
||||
async def handle_request_response(self, response: list[ChatMessage], ctx: WorkflowContext) -> None:
|
||||
"""Handler that processes the response from the RequestInfoExecutor."""
|
||||
# Update the chat history with the response
|
||||
self._chat_history.extend(response)
|
||||
|
||||
# Send the response to the other members
|
||||
await self._broadcast_message(response, ctx)
|
||||
|
||||
# Check for termination condition
|
||||
if self._should_terminate():
|
||||
await ctx.add_event(WorkflowCompletedEvent(data=response))
|
||||
return
|
||||
|
||||
# Request the next member to respond
|
||||
selection = self._get_next_member()
|
||||
await ctx.send_message(AgentExecutorRequest(messages=[], should_respond=True), target_id=selection)
|
||||
|
||||
async def _broadcast_message(
|
||||
self,
|
||||
messages: list[ChatMessage],
|
||||
ctx: WorkflowContext,
|
||||
exclude_id: str | None = None,
|
||||
) -> None:
|
||||
"""Broadcast messages to all members."""
|
||||
await asyncio.gather(*[
|
||||
ctx.send_message(
|
||||
AgentExecutorRequest(messages=messages, should_respond=False),
|
||||
target_id=member_id,
|
||||
)
|
||||
for member_id in self._members
|
||||
if member_id != exclude_id
|
||||
])
|
||||
|
||||
def _should_terminate(self) -> bool:
|
||||
"""Determine if the group chat should terminate based on the last message."""
|
||||
if len(self._chat_history) == 0:
|
||||
return False
|
||||
|
||||
last_message = self._chat_history[-1]
|
||||
return bool(last_message.role == ChatRole.USER and "approve" in last_message.text.lower())
|
||||
|
||||
def _should_request_info(self) -> bool:
|
||||
"""Determine if the group chat should request HIL based on the last message."""
|
||||
if len(self._chat_history) == 0:
|
||||
return True
|
||||
|
||||
last_message = self._chat_history[-1]
|
||||
return last_message.role == ChatRole.ASSISTANT
|
||||
|
||||
def _get_next_member(self) -> str:
|
||||
"""Get the next member in the round-robin sequence."""
|
||||
next_member = self._members[self._current_round % len(self._members)]
|
||||
self._current_round += 1
|
||||
|
||||
return next_member
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to run the group chat workflow."""
|
||||
# Step 1: Create the executors.
|
||||
writer = AgentExecutor(
|
||||
ChatClientAgent(
|
||||
AzureChatClient(),
|
||||
instructions=(
|
||||
"You are an excellent content writer. You create new content and edit contents based on the feedback."
|
||||
),
|
||||
name="Writer",
|
||||
id="Writer",
|
||||
),
|
||||
)
|
||||
reviewer = AgentExecutor(
|
||||
ChatClientAgent(
|
||||
AzureChatClient(),
|
||||
instructions=(
|
||||
"You are an excellent content reviewer. You review the content and provide feedback to the writer. "
|
||||
"You do not address user requests. Only provide feedback to the writer."
|
||||
),
|
||||
name="Reviewer",
|
||||
id="Reviewer",
|
||||
),
|
||||
)
|
||||
|
||||
group_chat_manager = CriticGroupChatManager(members=[writer.id, reviewer.id], id="GroupChatManager")
|
||||
|
||||
request_info_executor = RequestInfoExecutor()
|
||||
|
||||
# Step 2: Build the workflow with the defined edges.
|
||||
workflow = (
|
||||
WorkflowBuilder()
|
||||
.set_start_executor(group_chat_manager)
|
||||
.add_edge(group_chat_manager, request_info_executor)
|
||||
.add_edge(request_info_executor, group_chat_manager)
|
||||
.add_edge(group_chat_manager, writer)
|
||||
.add_edge(group_chat_manager, reviewer)
|
||||
.add_edge(writer, group_chat_manager)
|
||||
.add_edge(reviewer, group_chat_manager)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Step 3: Run the workflow with an initial message.
|
||||
# Here we are capturing the RequestInfoEvent event and allowing the user to provide input.
|
||||
# Once the user provides input, we will provide it back to the workflow to continue the execution.
|
||||
completion_event: WorkflowCompletedEvent | None = None
|
||||
request_info_event: RequestInfoEvent | None = None
|
||||
user_input = ""
|
||||
|
||||
while True:
|
||||
# Depending on whether we have a RequestInfoEvent event, we either
|
||||
# run the workflow normally or send the message to the HIL executor.
|
||||
if not request_info_event:
|
||||
response_stream = workflow.run_streaming(
|
||||
"Create a slogan for a new electric SUV that is affordable and fun to drive."
|
||||
)
|
||||
else:
|
||||
response_stream = workflow.send_responses_streaming({
|
||||
request_info_event.request_id: [ChatMessage(ChatRole.USER, text=user_input)]
|
||||
})
|
||||
request_info_event = None
|
||||
|
||||
async for event in response_stream:
|
||||
print(event)
|
||||
|
||||
if isinstance(event, WorkflowCompletedEvent):
|
||||
completion_event = event
|
||||
elif isinstance(event, RequestInfoEvent):
|
||||
request_info_event = event
|
||||
|
||||
# Prompt for user input if we are waiting for human intervention
|
||||
if request_info_event:
|
||||
user_input = input("Human feedback required. Please provide your input (type 'approve' to end): ")
|
||||
elif completion_event:
|
||||
break
|
||||
|
||||
print(f"Completion Event: {completion_event}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,312 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import ast
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
|
||||
import aiofiles
|
||||
from agent_framework.workflow import (
|
||||
Executor,
|
||||
WorkflowBuilder,
|
||||
WorkflowCompletedEvent,
|
||||
WorkflowContext,
|
||||
handler,
|
||||
)
|
||||
|
||||
if sys.version_info >= (3, 12):
|
||||
pass # pragma: no cover
|
||||
else:
|
||||
pass # pragma: no cover
|
||||
|
||||
|
||||
"""
|
||||
The following sample demonstrates a basic map reduce workflow that
|
||||
processes a large text file by splitting it into smaller chunks,
|
||||
mapping each word to a count, shuffling the results, and reducing them
|
||||
to a final count per word.
|
||||
|
||||
Intermediate results are stored in a temporary directory, and the
|
||||
final results are written to a file in the same directory.
|
||||
"""
|
||||
|
||||
# Define the temporary directory for storing intermediate results
|
||||
DIR = os.path.dirname(__file__)
|
||||
TEMP_DIR = os.path.join(DIR, "tmp")
|
||||
# Ensure the temporary directory exists
|
||||
os.makedirs(TEMP_DIR, exist_ok=True)
|
||||
|
||||
# Define a key for the shared state to store the data to be processed
|
||||
SHARED_STATE_DATA_KEY = "data_to_be_processed"
|
||||
|
||||
|
||||
class SplitCompleted:
|
||||
"""A class to signal the completion of the Split executor."""
|
||||
|
||||
...
|
||||
|
||||
|
||||
class Split(Executor):
|
||||
"""An executor that splits data into smaller chunks based on the number of nodes available."""
|
||||
|
||||
def __init__(self, map_executor_ids: list[str], id: str | None = None):
|
||||
"""Initialize the executor with the number of nodes."""
|
||||
super().__init__(id)
|
||||
self._map_executor_ids = map_executor_ids
|
||||
|
||||
@handler(output_types=[SplitCompleted])
|
||||
async def split(self, data: str, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by splitting the data into chunks.
|
||||
|
||||
Args:
|
||||
data: A string containing the text to be processed.
|
||||
ctx: The execution context containing the shared state and other information.
|
||||
"""
|
||||
# Process data into a list of words and remove empty lines/words.
|
||||
word_list = self._preprocess(data)
|
||||
|
||||
# Store the data to be processed state for later use.
|
||||
await ctx.set_shared_state(SHARED_STATE_DATA_KEY, word_list)
|
||||
|
||||
# Split the word_list into chunks that are represented by the start and end indices.
|
||||
# The start and end indices tuples will be stored in the shared state.
|
||||
map_executor_count = len(self._map_executor_ids)
|
||||
chunk_size = len(word_list) // map_executor_count # Assuming map_executor_count is not 0.
|
||||
|
||||
async def _process_chunk(i: int) -> None:
|
||||
"""Process each chunk and send a message to the executor."""
|
||||
start_index = i * chunk_size
|
||||
end_index = start_index + chunk_size if i < map_executor_count - 1 else len(word_list)
|
||||
|
||||
# The start and end indices are stored in the shared state for the MapExecutor.
|
||||
# This allows the MapExecutor to know which part of the data it should process.
|
||||
await ctx.set_shared_state(self._map_executor_ids[i], (start_index, end_index))
|
||||
await ctx.send_message(SplitCompleted(), self._map_executor_ids[i])
|
||||
|
||||
tasks = [asyncio.create_task(_process_chunk(i)) for i in range(map_executor_count)]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
def _preprocess(self, data: str) -> list[str]:
|
||||
"""Preprocess the input data and return a list of words.
|
||||
|
||||
Args:
|
||||
data: The input data to be processed.
|
||||
|
||||
Returns:
|
||||
A list of words extracted from the input data.
|
||||
"""
|
||||
line_list = [line.strip() for line in data.splitlines() if line.strip()]
|
||||
return [word for line in line_list for word in line.split() if word]
|
||||
|
||||
|
||||
@dataclass
|
||||
class MapCompleted:
|
||||
"""A data class to hold the completed state of the MapExecutor."""
|
||||
|
||||
file_path: str
|
||||
|
||||
|
||||
class Map(Executor):
|
||||
"""An executor that applies a function to each item in the data and save the result to a file."""
|
||||
|
||||
@handler(output_types=[MapCompleted])
|
||||
async def map(self, _: SplitCompleted, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by applying a function to each item and same result to a file.
|
||||
|
||||
Args:
|
||||
data: An instance of SplitCompleted signaling the map step can be started.
|
||||
ctx: The execution context containing the shared state and other information.
|
||||
"""
|
||||
# Retrieve the data to be processed from the shared state.# Define a key for the shared state to store the data to be processed
|
||||
data_to_be_processed: list[str] = await ctx.get_shared_state(SHARED_STATE_DATA_KEY)
|
||||
chunk_start, chunk_end = await ctx.get_shared_state(self.id)
|
||||
|
||||
results = [(item, 1) for item in data_to_be_processed[chunk_start:chunk_end]]
|
||||
|
||||
file_path = os.path.join(TEMP_DIR, f"map_results_{self.id}.txt")
|
||||
async with aiofiles.open(file_path, "w") as f:
|
||||
await f.writelines([f"{item}: {count}\n" for item, count in results])
|
||||
|
||||
await ctx.send_message(MapCompleted(file_path))
|
||||
|
||||
|
||||
@dataclass
|
||||
class ShuffleCompleted:
|
||||
"""A data class to hold the completed state of the ShuffleExecutor."""
|
||||
|
||||
file_path: str
|
||||
reducer_id: str
|
||||
|
||||
|
||||
class Shuffle(Executor):
|
||||
"""An executor that redistributes results from the map step to the reduce step."""
|
||||
|
||||
def __init__(self, reducer_ids: list[str], id: str | None = None):
|
||||
"""Initialize the executor with the number of nodes."""
|
||||
super().__init__(id)
|
||||
self._reducer_ids = reducer_ids
|
||||
|
||||
@handler(output_types=[ShuffleCompleted])
|
||||
async def shuffle(self, data: list[MapCompleted], ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by aggregating the results.
|
||||
|
||||
Args:
|
||||
data: A list of MapCompleted instances containing the file paths of the map results.
|
||||
ctx: The execution context containing the shared state and other information.
|
||||
"""
|
||||
chunks = await self._preprocess(data)
|
||||
|
||||
async def _process_chunk(chunk: list[tuple[str, list[int]]], index: int) -> None:
|
||||
"""Process each chunk and save it to a file."""
|
||||
file_path = os.path.join(TEMP_DIR, f"shuffle_results_{index}.txt")
|
||||
async with aiofiles.open(file_path, "w") as f:
|
||||
await f.writelines([f"{key}: {value}\n" for key, value in chunk])
|
||||
await ctx.send_message(ShuffleCompleted(file_path, self._reducer_ids[index]))
|
||||
|
||||
tasks = [asyncio.create_task(_process_chunk(chunk, i)) for i, chunk in enumerate(chunks)]
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
async def _preprocess(self, data: list[MapCompleted]) -> list[list[tuple[str, list[int]]]]:
|
||||
"""Preprocess the input data and return a list of data to be processed by the reduce executors.
|
||||
|
||||
Args:
|
||||
data: A list of MapCompleted instances containing the file paths of the map results.
|
||||
|
||||
Returns:
|
||||
A list of lists, where each inner list contains tuples of (key, value) pairs to be processed
|
||||
by the reduce executors.
|
||||
"""
|
||||
map_results: list[tuple[str, int]] = []
|
||||
for result in data:
|
||||
async with aiofiles.open(result.file_path, "r") as f:
|
||||
map_results.extend([
|
||||
(line.strip().split(": ")[0], int(line.strip().split(": ")[1])) for line in await f.readlines()
|
||||
])
|
||||
|
||||
# Group values by the first element
|
||||
intermediate_results: defaultdict[str, list[int]] = defaultdict(list[int])
|
||||
for item in map_results:
|
||||
key = item[0]
|
||||
value = item[1]
|
||||
intermediate_results[key].append(value)
|
||||
|
||||
# Convert defaultdict to a list
|
||||
aggregated_results = [(key, values) for key, values in intermediate_results.items()]
|
||||
|
||||
# Sort by the first element
|
||||
aggregated_results.sort(key=lambda x: x[0])
|
||||
|
||||
# Split the intermediate results into chunks for the reduce executors
|
||||
reduce_executor_count = len(self._reducer_ids)
|
||||
chunk_size = len(aggregated_results) // reduce_executor_count
|
||||
remaining = len(aggregated_results) % reduce_executor_count
|
||||
|
||||
chunks = [
|
||||
aggregated_results[i : i + chunk_size] for i in range(0, len(aggregated_results) - remaining, chunk_size)
|
||||
]
|
||||
# Append the remaining items to the last chunk
|
||||
if remaining > 0:
|
||||
chunks[-1].extend(aggregated_results[-remaining:])
|
||||
|
||||
return chunks
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReduceCompleted:
|
||||
"""A data class to hold the completed state of the ReduceExecutor."""
|
||||
|
||||
file_path: str
|
||||
|
||||
|
||||
class Reduce(Executor):
|
||||
"""An executor that reduces the results from the ShuffleExecutor."""
|
||||
|
||||
@handler(output_types=[ReduceCompleted])
|
||||
async def _execute(self, data: ShuffleCompleted, ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by reducing the results.
|
||||
|
||||
Args:
|
||||
data: An instance of ShuffleCompleted containing the file path of the shuffle results.
|
||||
ctx: The execution context containing the shared state and other information.
|
||||
"""
|
||||
if data.reducer_id != self.id:
|
||||
# If the reducer ID does not match, skip processing.
|
||||
return
|
||||
|
||||
# Read the intermediate results from the file
|
||||
async with aiofiles.open(data.file_path, "r") as f:
|
||||
lines = await f.readlines()
|
||||
|
||||
# Aggregate the results
|
||||
reduced_results: dict[str, int] = defaultdict(int)
|
||||
for line in lines:
|
||||
key, value = line.split(": ")
|
||||
reduced_results[key] = sum(ast.literal_eval(value))
|
||||
|
||||
# Write the reduced results to a file
|
||||
file_path = os.path.join(TEMP_DIR, f"reduced_results_{self.id}.txt")
|
||||
async with aiofiles.open(file_path, "w") as f:
|
||||
await f.writelines([f"{key}: {value}\n" for key, value in reduced_results.items()])
|
||||
|
||||
await ctx.send_message(ReduceCompleted(file_path))
|
||||
|
||||
|
||||
class CompletionExecutor(Executor):
|
||||
"""An executor that completes the workflow by aggregating the results from the ReduceExecutors."""
|
||||
|
||||
@handler
|
||||
async def complete(self, data: list[ReduceCompleted], ctx: WorkflowContext) -> None:
|
||||
"""Execute the task by aggregating the results.
|
||||
|
||||
Args:
|
||||
data: A list of ReduceCompleted instances containing the file paths of the reduced results.
|
||||
ctx: The execution context containing the shared state and other information.
|
||||
"""
|
||||
await ctx.add_event(WorkflowCompletedEvent(data=[result.file_path for result in data]))
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main function to run the workflow."""
|
||||
# Step 1: Create the executors.
|
||||
map_operations = [Map(id=f"map_executor_{i}") for i in range(3)]
|
||||
split_operation = Split(
|
||||
[map_operation.id for map_operation in map_operations],
|
||||
id="split_data_executor",
|
||||
)
|
||||
reduce_operations = [Reduce(id=f"reduce_executor_{i}") for i in range(4)]
|
||||
shuffle_operation = Shuffle(
|
||||
[reduce_operation.id for reduce_operation in reduce_operations],
|
||||
id="shuffle_executor",
|
||||
)
|
||||
completion_executor = CompletionExecutor(id="completion_executor")
|
||||
|
||||
# Step 2: Build the workflow.
|
||||
workflow = (
|
||||
WorkflowBuilder()
|
||||
.set_start_executor(split_operation)
|
||||
.add_fan_out_edges(split_operation, map_operations)
|
||||
.add_fan_in_edges(map_operations, shuffle_operation)
|
||||
.add_fan_out_edges(shuffle_operation, reduce_operations)
|
||||
.add_fan_in_edges(reduce_operations, completion_executor)
|
||||
.build()
|
||||
)
|
||||
|
||||
# Step 3: Open the text file and read its content.
|
||||
async with aiofiles.open(os.path.join(DIR, "resources", "long_text.txt"), "r") as f:
|
||||
raw_text = await f.read()
|
||||
|
||||
# Step 4: Run the workflow with the raw text as input.
|
||||
completion_event = None
|
||||
async for event in workflow.run_streaming(raw_text):
|
||||
print(f"Event: {event}")
|
||||
if isinstance(event, WorkflowCompletedEvent):
|
||||
completion_event = event
|
||||
|
||||
if completion_event:
|
||||
print(f"Completion Event: {completion_event}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user