#
tokens: 46194/50000 3/1784 files (page 90/145)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 90 of 145. Use http://codebase.md/microsoft/semanticworkbench?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .devcontainer
│   ├── .vscode
│   │   └── settings.json
│   ├── devcontainer.json
│   ├── OPTIMIZING_FOR_CODESPACES.md
│   ├── POST_SETUP_README.md
│   └── README.md
├── .dockerignore
├── .gitattributes
├── .github
│   ├── policheck.yml
│   └── workflows
│       ├── assistants-codespace-assistant.yml
│       ├── assistants-document-assistant.yml
│       ├── assistants-explorer-assistant.yml
│       ├── assistants-guided-conversation-assistant.yml
│       ├── assistants-knowledge-transfer-assistant.yml
│       ├── assistants-navigator-assistant.yml
│       ├── assistants-project-assistant.yml
│       ├── assistants-prospector-assistant.yml
│       ├── assistants-skill-assistant.yml
│       ├── libraries.yml
│       ├── mcp-server-giphy.yml
│       ├── mcp-server-memory-filesystem-edit.yml
│       ├── mcp-server-memory-user-bio.yml
│       ├── mcp-server-memory-whiteboard.yml
│       ├── mcp-server-open-deep-research-clone.yml
│       ├── mcp-server-web-research.yml
│       ├── workbench-app.yml
│       └── workbench-service.yml
├── .gitignore
├── .multi-root-tools
│   ├── Makefile
│   └── README.md
├── .vscode
│   ├── extensions.json
│   ├── launch.json
│   └── settings.json
├── ai_context
│   └── generated
│       ├── ASPIRE_ORCHESTRATOR.md
│       ├── ASSISTANT_CODESPACE.md
│       ├── ASSISTANT_DOCUMENT.md
│       ├── ASSISTANT_NAVIGATOR.md
│       ├── ASSISTANT_PROJECT.md
│       ├── ASSISTANT_PROSPECTOR.md
│       ├── ASSISTANTS_OTHER.md
│       ├── ASSISTANTS_OVERVIEW.md
│       ├── CONFIGURATION.md
│       ├── DOTNET_LIBRARIES.md
│       ├── EXAMPLES.md
│       ├── MCP_SERVERS.md
│       ├── PYTHON_LIBRARIES_AI_CLIENTS.md
│       ├── PYTHON_LIBRARIES_CORE.md
│       ├── PYTHON_LIBRARIES_EXTENSIONS.md
│       ├── PYTHON_LIBRARIES_SKILLS.md
│       ├── PYTHON_LIBRARIES_SPECIALIZED.md
│       ├── TOOLS.md
│       ├── WORKBENCH_FRONTEND.md
│       └── WORKBENCH_SERVICE.md
├── aspire-orchestrator
│   ├── .editorconfig
│   ├── Aspire.AppHost
│   │   ├── .gitignore
│   │   ├── appsettings.json
│   │   ├── Aspire.AppHost.csproj
│   │   ├── Program.cs
│   │   └── Properties
│   │       └── launchSettings.json
│   ├── Aspire.Extensions
│   │   ├── Aspire.Extensions.csproj
│   │   ├── Dashboard.cs
│   │   ├── DockerFileExtensions.cs
│   │   ├── PathNormalizer.cs
│   │   ├── UvAppHostingExtensions.cs
│   │   ├── UvAppResource.cs
│   │   ├── VirtualEnvironment.cs
│   │   └── WorkbenchServiceHostingExtensions.cs
│   ├── Aspire.ServiceDefaults
│   │   ├── Aspire.ServiceDefaults.csproj
│   │   └── Extensions.cs
│   ├── README.md
│   ├── run.sh
│   ├── SemanticWorkbench.Aspire.sln
│   └── SemanticWorkbench.Aspire.sln.DotSettings
├── assistants
│   ├── codespace-assistant
│   │   ├── .claude
│   │   │   └── settings.local.json
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── extensions.json
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── assets
│   │   │   │   ├── icon_context_transfer.svg
│   │   │   │   └── icon.svg
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── helpers.py
│   │   │   ├── response
│   │   │   │   ├── __init__.py
│   │   │   │   ├── completion_handler.py
│   │   │   │   ├── models.py
│   │   │   │   ├── request_builder.py
│   │   │   │   ├── response.py
│   │   │   │   ├── step_handler.py
│   │   │   │   └── utils
│   │   │   │       ├── __init__.py
│   │   │   │       ├── abbreviations.py
│   │   │   │       ├── formatting_utils.py
│   │   │   │       ├── message_utils.py
│   │   │   │       └── openai_utils.py
│   │   │   ├── text_includes
│   │   │   │   ├── card_content_context_transfer.md
│   │   │   │   ├── card_content.md
│   │   │   │   ├── codespace_assistant_info.md
│   │   │   │   ├── context_transfer_assistant_info.md
│   │   │   │   ├── guardrails_prompt.txt
│   │   │   │   ├── guidance_prompt_context_transfer.txt
│   │   │   │   ├── guidance_prompt.txt
│   │   │   │   ├── instruction_prompt_context_transfer.txt
│   │   │   │   └── instruction_prompt.txt
│   │   │   └── whiteboard
│   │   │       ├── __init__.py
│   │   │       ├── _inspector.py
│   │   │       └── _whiteboard.py
│   │   ├── assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── document-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── assets
│   │   │   │   └── icon.svg
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── context_management
│   │   │   │   ├── __init__.py
│   │   │   │   └── inspector.py
│   │   │   ├── filesystem
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _convert.py
│   │   │   │   ├── _file_sources.py
│   │   │   │   ├── _filesystem.py
│   │   │   │   ├── _inspector.py
│   │   │   │   ├── _model.py
│   │   │   │   ├── _prompts.py
│   │   │   │   └── _tasks.py
│   │   │   ├── guidance
│   │   │   │   ├── __init__.py
│   │   │   │   ├── dynamic_ui_inspector.py
│   │   │   │   ├── guidance_config.py
│   │   │   │   ├── guidance_prompts.py
│   │   │   │   └── README.md
│   │   │   ├── response
│   │   │   │   ├── __init__.py
│   │   │   │   ├── completion_handler.py
│   │   │   │   ├── models.py
│   │   │   │   ├── prompts.py
│   │   │   │   ├── responder.py
│   │   │   │   └── utils
│   │   │   │       ├── __init__.py
│   │   │   │       ├── formatting_utils.py
│   │   │   │       ├── message_utils.py
│   │   │   │       ├── openai_utils.py
│   │   │   │       ├── tokens_tiktoken.py
│   │   │   │       └── workbench_messages.py
│   │   │   ├── text_includes
│   │   │   │   └── document_assistant_info.md
│   │   │   ├── types.py
│   │   │   └── whiteboard
│   │   │       ├── __init__.py
│   │   │       ├── _inspector.py
│   │   │       └── _whiteboard.py
│   │   ├── assistant.code-workspace
│   │   ├── CLAUDE.md
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   ├── __init__.py
│   │   │   ├── test_convert.py
│   │   │   └── test_data
│   │   │       ├── blank_image.png
│   │   │       ├── Formatting Test.docx
│   │   │       ├── sample_data.csv
│   │   │       ├── sample_data.xlsx
│   │   │       ├── sample_page.html
│   │   │       ├── sample_presentation.pptx
│   │   │       └── simple_pdf.pdf
│   │   └── uv.lock
│   ├── explorer-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── helpers.py
│   │   │   ├── response
│   │   │   │   ├── __init__.py
│   │   │   │   ├── model.py
│   │   │   │   ├── response_anthropic.py
│   │   │   │   ├── response_openai.py
│   │   │   │   └── response.py
│   │   │   └── text_includes
│   │   │       └── guardrails_prompt.txt
│   │   ├── assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── guided-conversation-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── agents
│   │   │   │   ├── guided_conversation
│   │   │   │   │   ├── config.py
│   │   │   │   │   ├── definition.py
│   │   │   │   │   └── definitions
│   │   │   │   │       ├── er_triage.py
│   │   │   │   │       ├── interview.py
│   │   │   │   │       ├── patient_intake.py
│   │   │   │   │       └── poem_feedback.py
│   │   │   │   └── guided_conversation_agent.py
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   └── text_includes
│   │   │       └── guardrails_prompt.txt
│   │   ├── assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── knowledge-transfer-assistant
│   │   ├── .claude
│   │   │   └── settings.local.json
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── agentic
│   │   │   │   ├── __init__.py
│   │   │   │   ├── analysis.py
│   │   │   │   ├── coordinator_support.py
│   │   │   │   └── team_welcome.py
│   │   │   ├── assets
│   │   │   │   ├── icon-knowledge-transfer.svg
│   │   │   │   └── icon.svg
│   │   │   ├── assistant.py
│   │   │   ├── common.py
│   │   │   ├── config.py
│   │   │   ├── conversation_clients.py
│   │   │   ├── conversation_share_link.py
│   │   │   ├── data.py
│   │   │   ├── domain
│   │   │   │   ├── __init__.py
│   │   │   │   ├── audience_manager.py
│   │   │   │   ├── information_request_manager.py
│   │   │   │   ├── knowledge_brief_manager.py
│   │   │   │   ├── knowledge_digest_manager.py
│   │   │   │   ├── learning_objectives_manager.py
│   │   │   │   └── share_manager.py
│   │   │   ├── files.py
│   │   │   ├── logging.py
│   │   │   ├── notifications.py
│   │   │   ├── respond.py
│   │   │   ├── storage_models.py
│   │   │   ├── storage.py
│   │   │   ├── string_utils.py
│   │   │   ├── text_includes
│   │   │   │   ├── assistant_info.md
│   │   │   │   ├── card_content.md
│   │   │   │   ├── coordinator_instructions.txt
│   │   │   │   ├── coordinator_role.txt
│   │   │   │   ├── knowledge_digest_instructions.txt
│   │   │   │   ├── knowledge_digest_prompt.txt
│   │   │   │   ├── share_information_request_detection.txt
│   │   │   │   ├── team_instructions.txt
│   │   │   │   ├── team_role.txt
│   │   │   │   └── welcome_message_generation.txt
│   │   │   ├── tools
│   │   │   │   ├── __init__.py
│   │   │   │   ├── base.py
│   │   │   │   ├── information_requests.py
│   │   │   │   ├── learning_objectives.py
│   │   │   │   ├── learning_outcomes.py
│   │   │   │   ├── progress_tracking.py
│   │   │   │   └── share_setup.py
│   │   │   ├── ui_tabs
│   │   │   │   ├── __init__.py
│   │   │   │   ├── brief.py
│   │   │   │   ├── common.py
│   │   │   │   ├── debug.py
│   │   │   │   ├── learning.py
│   │   │   │   └── sharing.py
│   │   │   └── utils.py
│   │   ├── CLAUDE.md
│   │   ├── docs
│   │   │   ├── design
│   │   │   │   ├── actions.md
│   │   │   │   └── inference.md
│   │   │   ├── DEV_GUIDE.md
│   │   │   ├── how-kta-works.md
│   │   │   ├── JTBD.md
│   │   │   ├── knowledge-transfer-goals.md
│   │   │   ├── learning_assistance.md
│   │   │   ├── notable_claude_conversations
│   │   │   │   ├── clarifying_quad_modal_design.md
│   │   │   │   ├── CLAUDE_PROMPTS.md
│   │   │   │   ├── transfer_state.md
│   │   │   │   └── trying_the_context_agent.md
│   │   │   └── opportunities-of-knowledge-transfer.md
│   │   ├── knowledge-transfer-assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   ├── __init__.py
│   │   │   ├── test_artifact_loading.py
│   │   │   ├── test_inspector.py
│   │   │   ├── test_share_manager.py
│   │   │   ├── test_share_storage.py
│   │   │   ├── test_share_tools.py
│   │   │   └── test_team_mode.py
│   │   └── uv.lock
│   ├── Makefile
│   ├── navigator-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── assets
│   │   │   │   ├── card_content.md
│   │   │   │   └── icon.svg
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── helpers.py
│   │   │   ├── response
│   │   │   │   ├── __init__.py
│   │   │   │   ├── completion_handler.py
│   │   │   │   ├── completion_requestor.py
│   │   │   │   ├── local_tool
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── add_assistant_to_conversation.py
│   │   │   │   │   ├── list_assistant_services.py
│   │   │   │   │   └── model.py
│   │   │   │   ├── models.py
│   │   │   │   ├── prompt.py
│   │   │   │   ├── request_builder.py
│   │   │   │   ├── response.py
│   │   │   │   ├── step_handler.py
│   │   │   │   └── utils
│   │   │   │       ├── __init__.py
│   │   │   │       ├── formatting_utils.py
│   │   │   │       ├── message_utils.py
│   │   │   │       ├── openai_utils.py
│   │   │   │       └── tools.py
│   │   │   ├── text_includes
│   │   │   │   ├── guardrails_prompt.md
│   │   │   │   ├── guidance_prompt.md
│   │   │   │   ├── instruction_prompt.md
│   │   │   │   ├── navigator_assistant_info.md
│   │   │   │   └── semantic_workbench_features.md
│   │   │   └── whiteboard
│   │   │       ├── __init__.py
│   │   │       ├── _inspector.py
│   │   │       └── _whiteboard.py
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── project-assistant
│   │   ├── .cspell
│   │   │   └── custom-dictionary-workspace.txt
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── agentic
│   │   │   │   ├── __init__.py
│   │   │   │   ├── act.py
│   │   │   │   ├── coordinator_next_action.py
│   │   │   │   ├── create_invitation.py
│   │   │   │   ├── detect_audience_and_takeaways.py
│   │   │   │   ├── detect_coordinator_actions.py
│   │   │   │   ├── detect_information_request_needs.py
│   │   │   │   ├── detect_knowledge_package_gaps.py
│   │   │   │   ├── focus.py
│   │   │   │   ├── respond.py
│   │   │   │   ├── team_welcome.py
│   │   │   │   └── update_digest.py
│   │   │   ├── assets
│   │   │   │   ├── icon-knowledge-transfer.svg
│   │   │   │   └── icon.svg
│   │   │   ├── assistant.py
│   │   │   ├── common.py
│   │   │   ├── config.py
│   │   │   ├── conversation_clients.py
│   │   │   ├── data.py
│   │   │   ├── domain
│   │   │   │   ├── __init__.py
│   │   │   │   ├── audience_manager.py
│   │   │   │   ├── conversation_preferences_manager.py
│   │   │   │   ├── information_request_manager.py
│   │   │   │   ├── knowledge_brief_manager.py
│   │   │   │   ├── knowledge_digest_manager.py
│   │   │   │   ├── learning_objectives_manager.py
│   │   │   │   ├── share_manager.py
│   │   │   │   ├── tasks_manager.py
│   │   │   │   └── transfer_manager.py
│   │   │   ├── errors.py
│   │   │   ├── files.py
│   │   │   ├── logging.py
│   │   │   ├── notifications.py
│   │   │   ├── prompt_utils.py
│   │   │   ├── storage.py
│   │   │   ├── string_utils.py
│   │   │   ├── text_includes
│   │   │   │   ├── actor_instructions.md
│   │   │   │   ├── assistant_info.md
│   │   │   │   ├── card_content.md
│   │   │   │   ├── coordinator_instructions copy.md
│   │   │   │   ├── coordinator_instructions.md
│   │   │   │   ├── create_invitation.md
│   │   │   │   ├── detect_audience.md
│   │   │   │   ├── detect_coordinator_actions.md
│   │   │   │   ├── detect_information_request_needs.md
│   │   │   │   ├── detect_knowledge_package_gaps.md
│   │   │   │   ├── focus.md
│   │   │   │   ├── knowledge_digest_instructions.txt
│   │   │   │   ├── team_instructions.txt
│   │   │   │   ├── to_do.md
│   │   │   │   ├── update_knowledge_brief.md
│   │   │   │   ├── update_knowledge_digest.md
│   │   │   │   └── welcome_message_generation.txt
│   │   │   ├── tools
│   │   │   │   ├── __init__.py
│   │   │   │   ├── base.py
│   │   │   │   ├── conversation_preferences.py
│   │   │   │   ├── information_requests.py
│   │   │   │   ├── learning_objectives.py
│   │   │   │   ├── learning_outcomes.py
│   │   │   │   ├── progress_tracking.py
│   │   │   │   ├── share_setup.py
│   │   │   │   ├── system_reminders.py
│   │   │   │   ├── tasks.py
│   │   │   │   └── todo.py
│   │   │   ├── ui_tabs
│   │   │   │   ├── __init__.py
│   │   │   │   ├── brief.py
│   │   │   │   ├── common.py
│   │   │   │   ├── debug.py
│   │   │   │   ├── learning.py
│   │   │   │   └── sharing.py
│   │   │   └── utils.py
│   │   ├── CLAUDE.md
│   │   ├── docs
│   │   │   ├── design
│   │   │   │   ├── actions.md
│   │   │   │   ├── control_options.md
│   │   │   │   ├── design.md
│   │   │   │   ├── inference.md
│   │   │   │   └── PXL_20250814_190140267.jpg
│   │   │   ├── DEV_GUIDE.md
│   │   │   ├── how-kta-works.md
│   │   │   ├── JTBD.md
│   │   │   ├── knowledge-transfer-goals.md
│   │   │   ├── learning_assistance.md
│   │   │   ├── notable_claude_conversations
│   │   │   │   ├── clarifying_quad_modal_design.md
│   │   │   │   ├── CLAUDE_PROMPTS.md
│   │   │   │   ├── transfer_state.md
│   │   │   │   └── trying_the_context_agent.md
│   │   │   └── opportunities-of-knowledge-transfer.md
│   │   ├── knowledge-transfer-assistant.code-workspace
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   ├── __init__.py
│   │   │   ├── test_artifact_loading.py
│   │   │   ├── test_inspector.py
│   │   │   ├── test_share_manager.py
│   │   │   ├── test_share_storage.py
│   │   │   └── test_team_mode.py
│   │   └── uv.lock
│   ├── prospector-assistant
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── assistant
│   │   │   ├── __init__.py
│   │   │   ├── agents
│   │   │   │   ├── artifact_agent.py
│   │   │   │   ├── document
│   │   │   │   │   ├── config.py
│   │   │   │   │   ├── gc_draft_content_feedback_config.py
│   │   │   │   │   ├── gc_draft_outline_feedback_config.py
│   │   │   │   │   ├── guided_conversation.py
│   │   │   │   │   └── state.py
│   │   │   │   └── document_agent.py
│   │   │   ├── artifact_creation_extension
│   │   │   │   ├── __init__.py
│   │   │   │   ├── _llm.py
│   │   │   │   ├── config.py
│   │   │   │   ├── document.py
│   │   │   │   ├── extension.py
│   │   │   │   ├── store.py
│   │   │   │   ├── test
│   │   │   │   │   ├── conftest.py
│   │   │   │   │   ├── evaluation.py
│   │   │   │   │   ├── test_completion_with_tools.py
│   │   │   │   │   └── test_extension.py
│   │   │   │   └── tools.py
│   │   │   ├── chat.py
│   │   │   ├── config.py
│   │   │   ├── form_fill_extension
│   │   │   │   ├── __init__.py
│   │   │   │   ├── config.py
│   │   │   │   ├── extension.py
│   │   │   │   ├── inspector.py
│   │   │   │   ├── state.py
│   │   │   │   └── steps
│   │   │   │       ├── __init__.py
│   │   │   │       ├── _guided_conversation.py
│   │   │   │       ├── _llm.py
│   │   │   │       ├── acquire_form_step.py
│   │   │   │       ├── extract_form_fields_step.py
│   │   │   │       ├── fill_form_step.py
│   │   │   │       └── types.py
│   │   │   ├── helpers.py
│   │   │   ├── legacy.py
│   │   │   └── text_includes
│   │   │       ├── artifact_agent_enabled.md
│   │   │       ├── guardrails_prompt.txt
│   │   │       ├── guided_conversation_agent_enabled.md
│   │   │       └── skills_agent_enabled.md
│   │   ├── assistant.code-workspace
│   │   ├── gc_learnings
│   │   │   ├── gc_learnings.md
│   │   │   └── images
│   │   │       ├── gc_conversation_plan_fcn.png
│   │   │       ├── gc_conversation_plan_template.png
│   │   │       ├── gc_execute_plan_callstack.png
│   │   │       ├── gc_functions.png
│   │   │       ├── gc_generate_plan_callstack.png
│   │   │       ├── gc_get_resource_instructions.png
│   │   │       ├── gc_get_termination_instructions.png
│   │   │       ├── gc_kernel_arguments.png
│   │   │       ├── gc_plan_calls.png
│   │   │       ├── gc_termination_instructions.png
│   │   │       ├── sk_get_chat_message_contents.png
│   │   │       ├── sk_inner_get_chat_message_contents.png
│   │   │       ├── sk_send_request_prep.png
│   │   │       └── sk_send_request.png
│   │   ├── Makefile
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   └── skill-assistant
│       ├── .env.example
│       ├── .vscode
│       │   ├── launch.json
│       │   └── settings.json
│       ├── assistant
│       │   ├── __init__.py
│       │   ├── config.py
│       │   ├── logging.py
│       │   ├── skill_assistant.py
│       │   ├── skill_engine_registry.py
│       │   ├── skill_event_mapper.py
│       │   ├── text_includes
│       │   │   └── guardrails_prompt.txt
│       │   └── workbench_helpers.py
│       ├── assistant.code-workspace
│       ├── Makefile
│       ├── pyproject.toml
│       ├── README.md
│       ├── tests
│       │   └── test_setup.py
│       └── uv.lock
├── CLAUDE.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── docs
│   ├── .vscode
│   │   └── settings.json
│   ├── ASSISTANT_CONFIG.md
│   ├── ASSISTANT_DEVELOPMENT_GUIDE.md
│   ├── CUSTOM_APP_REGISTRATION.md
│   ├── HOSTED_ASSISTANT_WITH_LOCAL_MCP_SERVERS.md
│   ├── images
│   │   ├── architecture-animation.gif
│   │   ├── configure_assistant.png
│   │   ├── conversation_canvas_open.png
│   │   ├── conversation_duplicate.png
│   │   ├── conversation_export.png
│   │   ├── conversation_share_dialog.png
│   │   ├── conversation_share_link.png
│   │   ├── dashboard_configured_view.png
│   │   ├── dashboard_view.png
│   │   ├── license_agreement.png
│   │   ├── message_bar.png
│   │   ├── message_inspection.png
│   │   ├── message_link.png
│   │   ├── new_prospector_assistant_dialog.png
│   │   ├── open_conversation_canvas.png
│   │   ├── prospector_example.png
│   │   ├── readme1.png
│   │   ├── readme2.png
│   │   ├── readme3.png
│   │   ├── rewind.png
│   │   ├── signin_page.png
│   │   └── splash_screen.png
│   ├── LOCAL_ASSISTANT_WITH_REMOTE_WORKBENCH.md
│   ├── SETUP_DEV_ENVIRONMENT.md
│   └── WORKBENCH_APP.md
├── examples
│   ├── dotnet
│   │   ├── .editorconfig
│   │   ├── dotnet-01-echo-bot
│   │   │   ├── appsettings.json
│   │   │   ├── dotnet-01-echo-bot.csproj
│   │   │   ├── MyAgent.cs
│   │   │   ├── MyAgentConfig.cs
│   │   │   ├── MyWorkbenchConnector.cs
│   │   │   ├── Program.cs
│   │   │   └── README.md
│   │   ├── dotnet-02-message-types-demo
│   │   │   ├── appsettings.json
│   │   │   ├── ConnectorExtensions.cs
│   │   │   ├── docs
│   │   │   │   ├── abc.png
│   │   │   │   ├── code.png
│   │   │   │   ├── config.png
│   │   │   │   ├── echo.png
│   │   │   │   ├── markdown.png
│   │   │   │   ├── mermaid.png
│   │   │   │   ├── reverse.png
│   │   │   │   └── safety-check.png
│   │   │   ├── dotnet-02-message-types-demo.csproj
│   │   │   ├── MyAgent.cs
│   │   │   ├── MyAgentConfig.cs
│   │   │   ├── MyWorkbenchConnector.cs
│   │   │   ├── Program.cs
│   │   │   └── README.md
│   │   └── dotnet-03-simple-chatbot
│   │       ├── appsettings.json
│   │       ├── ConnectorExtensions.cs
│   │       ├── dotnet-03-simple-chatbot.csproj
│   │       ├── MyAgent.cs
│   │       ├── MyAgentConfig.cs
│   │       ├── MyWorkbenchConnector.cs
│   │       ├── Program.cs
│   │       └── README.md
│   ├── Makefile
│   └── python
│       ├── python-01-echo-bot
│       │   ├── .env.example
│       │   ├── .vscode
│       │   │   ├── launch.json
│       │   │   └── settings.json
│       │   ├── assistant
│       │   │   ├── __init__.py
│       │   │   ├── chat.py
│       │   │   └── config.py
│       │   ├── assistant.code-workspace
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── python-02-simple-chatbot
│       │   ├── .env.example
│       │   ├── .vscode
│       │   │   ├── launch.json
│       │   │   └── settings.json
│       │   ├── assistant
│       │   │   ├── __init__.py
│       │   │   ├── chat.py
│       │   │   ├── config.py
│       │   │   └── text_includes
│       │   │       └── guardrails_prompt.txt
│       │   ├── assistant.code-workspace
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       └── python-03-multimodel-chatbot
│           ├── .env.example
│           ├── .vscode
│           │   ├── launch.json
│           │   └── settings.json
│           ├── assistant
│           │   ├── __init__.py
│           │   ├── chat.py
│           │   ├── config.py
│           │   ├── model_adapters.py
│           │   └── text_includes
│           │       └── guardrails_prompt.txt
│           ├── assistant.code-workspace
│           ├── Makefile
│           ├── pyproject.toml
│           ├── README.md
│           └── uv.lock
├── KNOWN_ISSUES.md
├── libraries
│   ├── dotnet
│   │   ├── .editorconfig
│   │   ├── pack.sh
│   │   ├── README.md
│   │   ├── SemanticWorkbench.sln
│   │   ├── SemanticWorkbench.sln.DotSettings
│   │   └── WorkbenchConnector
│   │       ├── AgentBase.cs
│   │       ├── AgentConfig
│   │       │   ├── AgentConfigBase.cs
│   │       │   ├── AgentConfigPropertyAttribute.cs
│   │       │   └── ConfigUtils.cs
│   │       ├── Constants.cs
│   │       ├── IAgentBase.cs
│   │       ├── icon.png
│   │       ├── Models
│   │       │   ├── Command.cs
│   │       │   ├── Conversation.cs
│   │       │   ├── ConversationEvent.cs
│   │       │   ├── DebugInfo.cs
│   │       │   ├── Insight.cs
│   │       │   ├── Message.cs
│   │       │   ├── MessageMetadata.cs
│   │       │   ├── Participant.cs
│   │       │   ├── Sender.cs
│   │       │   └── ServiceInfo.cs
│   │       ├── Storage
│   │       │   ├── AgentInfo.cs
│   │       │   ├── AgentServiceStorage.cs
│   │       │   └── IAgentServiceStorage.cs
│   │       ├── StringLoggingExtensions.cs
│   │       ├── Webservice.cs
│   │       ├── WorkbenchConfig.cs
│   │       ├── WorkbenchConnector.cs
│   │       └── WorkbenchConnector.csproj
│   ├── Makefile
│   └── python
│       ├── anthropic-client
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── anthropic_client
│       │   │   ├── __init__.py
│       │   │   ├── client.py
│       │   │   ├── config.py
│       │   │   └── messages.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── assistant-data-gen
│       │   ├── .vscode
│       │   │   ├── launch.json
│       │   │   └── settings.json
│       │   ├── assistant_data_gen
│       │   │   ├── __init__.py
│       │   │   ├── assistant_api.py
│       │   │   ├── config.py
│       │   │   ├── gce
│       │   │   │   ├── __init__.py
│       │   │   │   ├── gce_agent.py
│       │   │   │   └── prompts.py
│       │   │   └── pydantic_ai_utils.py
│       │   ├── configs
│       │   │   └── document_assistant_example_config.yaml
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── scripts
│       │   │   ├── gce_simulation.py
│       │   │   └── generate_scenario.py
│       │   └── uv.lock
│       ├── assistant-drive
│       │   ├── .gitignore
│       │   ├── .vscode
│       │   │   ├── extensions.json
│       │   │   └── settings.json
│       │   ├── assistant_drive
│       │   │   ├── __init__.py
│       │   │   ├── drive.py
│       │   │   └── tests
│       │   │       └── test_basic.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── pytest.ini
│       │   ├── README.md
│       │   ├── usage.ipynb
│       │   └── uv.lock
│       ├── assistant-extensions
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── assistant_extensions
│       │   │   ├── __init__.py
│       │   │   ├── ai_clients
│       │   │   │   └── config.py
│       │   │   ├── artifacts
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _artifacts.py
│       │   │   │   ├── _inspector.py
│       │   │   │   └── _model.py
│       │   │   ├── attachments
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _attachments.py
│       │   │   │   ├── _convert.py
│       │   │   │   ├── _model.py
│       │   │   │   ├── _shared.py
│       │   │   │   └── _summarizer.py
│       │   │   ├── chat_context_toolkit
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _config.py
│       │   │   │   ├── archive
│       │   │   │   │   ├── __init__.py
│       │   │   │   │   ├── _archive.py
│       │   │   │   │   └── _summarizer.py
│       │   │   │   ├── message_history
│       │   │   │   │   ├── __init__.py
│       │   │   │   │   ├── _history.py
│       │   │   │   │   └── _message.py
│       │   │   │   └── virtual_filesystem
│       │   │   │       ├── __init__.py
│       │   │   │       ├── _archive_file_source.py
│       │   │   │       └── _attachments_file_source.py
│       │   │   ├── dashboard_card
│       │   │   │   ├── __init__.py
│       │   │   │   └── _dashboard_card.py
│       │   │   ├── document_editor
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _extension.py
│       │   │   │   ├── _inspector.py
│       │   │   │   └── _model.py
│       │   │   ├── mcp
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _assistant_file_resource_handler.py
│       │   │   │   ├── _client_utils.py
│       │   │   │   ├── _devtunnel.py
│       │   │   │   ├── _model.py
│       │   │   │   ├── _openai_utils.py
│       │   │   │   ├── _sampling_handler.py
│       │   │   │   ├── _tool_utils.py
│       │   │   │   └── _workbench_file_resource_handler.py
│       │   │   ├── navigator
│       │   │   │   ├── __init__.py
│       │   │   │   └── _navigator.py
│       │   │   └── workflows
│       │   │       ├── __init__.py
│       │   │       ├── _model.py
│       │   │       ├── _workflows.py
│       │   │       └── runners
│       │   │           └── _user_proxy.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── test
│       │   │   └── attachments
│       │   │       └── test_attachments.py
│       │   └── uv.lock
│       ├── chat-context-toolkit
│       │   ├── .claude
│       │   │   └── settings.local.json
│       │   ├── .env.sample
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── assets
│       │   │   ├── archive_v1.png
│       │   │   ├── history_v1.png
│       │   │   └── vfs_v1.png
│       │   ├── chat_context_toolkit
│       │   │   ├── __init__.py
│       │   │   ├── archive
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _archive_reader.py
│       │   │   │   ├── _archive_task_queue.py
│       │   │   │   ├── _state.py
│       │   │   │   ├── _types.py
│       │   │   │   └── summarization
│       │   │   │       ├── __init__.py
│       │   │   │       └── _summarizer.py
│       │   │   ├── history
│       │   │   │   ├── __init__.py
│       │   │   │   ├── _budget.py
│       │   │   │   ├── _decorators.py
│       │   │   │   ├── _history.py
│       │   │   │   ├── _prioritize.py
│       │   │   │   ├── _types.py
│       │   │   │   └── tool_abbreviations
│       │   │   │       ├── __init__.py
│       │   │   │       └── _tool_abbreviations.py
│       │   │   └── virtual_filesystem
│       │   │       ├── __init__.py
│       │   │       ├── _types.py
│       │   │       ├── _virtual_filesystem.py
│       │   │       ├── README.md
│       │   │       └── tools
│       │   │           ├── __init__.py
│       │   │           ├── _ls_tool.py
│       │   │           ├── _tools.py
│       │   │           └── _view_tool.py
│       │   ├── CLAUDE.md
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── test
│       │   │   ├── archive
│       │   │   │   └── test_archive_reader.py
│       │   │   ├── history
│       │   │   │   ├── test_abbreviate_messages.py
│       │   │   │   ├── test_history.py
│       │   │   │   ├── test_pair_and_order_tool_messages.py
│       │   │   │   ├── test_prioritize.py
│       │   │   │   └── test_truncate_messages.py
│       │   │   └── virtual_filesystem
│       │   │       ├── test_virtual_filesystem.py
│       │   │       └── tools
│       │   │           ├── test_ls_tool.py
│       │   │           ├── test_tools.py
│       │   │           └── test_view_tool.py
│       │   └── uv.lock
│       ├── content-safety
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── content_safety
│       │   │   ├── __init__.py
│       │   │   ├── evaluators
│       │   │   │   ├── __init__.py
│       │   │   │   ├── azure_content_safety
│       │   │   │   │   ├── __init__.py
│       │   │   │   │   ├── config.py
│       │   │   │   │   └── evaluator.py
│       │   │   │   ├── config.py
│       │   │   │   ├── evaluator.py
│       │   │   │   └── openai_moderations
│       │   │   │       ├── __init__.py
│       │   │   │       ├── config.py
│       │   │   │       └── evaluator.py
│       │   │   └── README.md
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── events
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── events
│       │   │   ├── __init__.py
│       │   │   └── events.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── guided-conversation
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── guided_conversation
│       │   │   ├── __init__.py
│       │   │   ├── functions
│       │   │   │   ├── __init__.py
│       │   │   │   ├── conversation_plan.py
│       │   │   │   ├── execution.py
│       │   │   │   └── final_update_plan.py
│       │   │   ├── guided_conversation_agent.py
│       │   │   ├── plugins
│       │   │   │   ├── __init__.py
│       │   │   │   ├── agenda.py
│       │   │   │   └── artifact.py
│       │   │   └── utils
│       │   │       ├── __init__.py
│       │   │       ├── base_model_llm.py
│       │   │       ├── conversation_helpers.py
│       │   │       ├── openai_tool_calling.py
│       │   │       ├── plugin_helpers.py
│       │   │       └── resources.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── llm-client
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── llm_client
│       │   │   ├── __init__.py
│       │   │   └── model.py
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── Makefile
│       ├── mcp-extensions
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── mcp_extensions
│       │   │   ├── __init__.py
│       │   │   ├── _client_session.py
│       │   │   ├── _model.py
│       │   │   ├── _sampling.py
│       │   │   ├── _server_extensions.py
│       │   │   ├── _tool_utils.py
│       │   │   ├── llm
│       │   │   │   ├── __init__.py
│       │   │   │   ├── chat_completion.py
│       │   │   │   ├── helpers.py
│       │   │   │   ├── llm_types.py
│       │   │   │   ├── mcp_chat_completion.py
│       │   │   │   └── openai_chat_completion.py
│       │   │   └── server
│       │   │       ├── __init__.py
│       │   │       └── storage.py
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── tests
│       │   │   └── test_tool_utils.py
│       │   └── uv.lock
│       ├── mcp-tunnel
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── mcp_tunnel
│       │   │   ├── __init__.py
│       │   │   ├── _devtunnel.py
│       │   │   ├── _dir.py
│       │   │   └── _main.py
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   └── uv.lock
│       ├── openai-client
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── openai_client
│       │   │   ├── __init__.py
│       │   │   ├── chat_driver
│       │   │   │   ├── __init__.py
│       │   │   │   ├── chat_driver.ipynb
│       │   │   │   ├── chat_driver.py
│       │   │   │   ├── message_history_providers
│       │   │   │   │   ├── __init__.py
│       │   │   │   │   ├── in_memory_message_history_provider.py
│       │   │   │   │   ├── local_message_history_provider.py
│       │   │   │   │   ├── message_history_provider.py
│       │   │   │   │   └── tests
│       │   │   │   │       └── formatted_instructions_test.py
│       │   │   │   └── README.md
│       │   │   ├── client.py
│       │   │   ├── completion.py
│       │   │   ├── config.py
│       │   │   ├── errors.py
│       │   │   ├── logging.py
│       │   │   ├── messages.py
│       │   │   ├── tokens.py
│       │   │   └── tools.py
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── tests
│       │   │   ├── test_command_parsing.py
│       │   │   ├── test_formatted_messages.py
│       │   │   ├── test_messages.py
│       │   │   └── test_tokens.py
│       │   └── uv.lock
│       ├── semantic-workbench-api-model
│       │   ├── .vscode
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── semantic_workbench_api_model
│       │   │   ├── __init__.py
│       │   │   ├── assistant_model.py
│       │   │   ├── assistant_service_client.py
│       │   │   ├── workbench_model.py
│       │   │   └── workbench_service_client.py
│       │   └── uv.lock
│       ├── semantic-workbench-assistant
│       │   ├── .vscode
│       │   │   ├── launch.json
│       │   │   └── settings.json
│       │   ├── Makefile
│       │   ├── pyproject.toml
│       │   ├── README.md
│       │   ├── semantic_workbench_assistant
│       │   │   ├── __init__.py
│       │   │   ├── assistant_app
│       │   │   │   ├── __init__.py
│       │   │   │   ├── assistant.py
│       │   │   │   ├── config.py
│       │   │   │   ├── content_safety.py
│       │   │   │   ├── context.py
│       │   │   │   ├── error.py
│       │   │   │   ├── export_import.py
│       │   │   │   ├── protocol.py
│       │   │   │   └── service.py
│       │   │   ├── assistant_service.py
│       │   │   ├── auth.py
│       │   │   ├── canonical.py
│       │   │   ├── command.py
│       │   │   ├── config.py
│       │   │   ├── logging_config.py
│       │   │   ├── settings.py
│       │   │   ├── start.py
│       │   │   └── storage.py
│       │   ├── tests
│       │   │   ├── conftest.py
│       │   │   ├── test_assistant_app.py
│       │   │   ├── test_canonical.py
│       │   │   ├── test_config.py
│       │   │   └── test_storage.py
│       │   └── uv.lock
│       └── skills
│           ├── .vscode
│           │   └── settings.json
│           ├── Makefile
│           ├── README.md
│           └── skill-library
│               ├── .vscode
│               │   └── settings.json
│               ├── docs
│               │   └── vs-recipe-tool.md
│               ├── Makefile
│               ├── pyproject.toml
│               ├── README.md
│               ├── skill_library
│               │   ├── __init__.py
│               │   ├── chat_driver_helpers.py
│               │   ├── cli
│               │   │   ├── azure_openai.py
│               │   │   ├── conversation_history.py
│               │   │   ├── README.md
│               │   │   ├── run_routine.py
│               │   │   ├── settings.py
│               │   │   └── skill_logger.py
│               │   ├── engine.py
│               │   ├── llm_info.txt
│               │   ├── logging.py
│               │   ├── README.md
│               │   ├── routine_stack.py
│               │   ├── skill.py
│               │   ├── skills
│               │   │   ├── common
│               │   │   │   ├── __init__.py
│               │   │   │   ├── common_skill.py
│               │   │   │   └── routines
│               │   │   │       ├── bing_search.py
│               │   │   │       ├── consolidate.py
│               │   │   │       ├── echo.py
│               │   │   │       ├── gather_context.py
│               │   │   │       ├── get_content_from_url.py
│               │   │   │       ├── gpt_complete.py
│               │   │   │       ├── select_user_intent.py
│               │   │   │       └── summarize.py
│               │   │   ├── eval
│               │   │   │   ├── __init__.py
│               │   │   │   ├── eval_skill.py
│               │   │   │   └── routines
│               │   │   │       └── eval.py
│               │   │   ├── fabric
│               │   │   │   ├── __init__.py
│               │   │   │   ├── fabric_skill.py
│               │   │   │   ├── patterns
│               │   │   │   │   ├── agility_story
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── ai
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_answers
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_candidates
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_cfp_submission
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_claims
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_comments
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_debate
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_email_headers
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_incident
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_interviewer_techniques
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_logs
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_malware
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_military_strategy
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_mistakes
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_paper
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_patent
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_personality
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_presentation
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_product_feedback
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_proposition
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_prose
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_prose_json
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_prose_pinker
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_risk
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_sales_call
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_spiritual_text
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_tech_impact
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_threat_report
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── analyze_threat_report_cmds
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── analyze_threat_report_trends
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── answer_interview_question
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── ask_secure_by_design_questions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── ask_uncle_duke
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── capture_thinkers_work
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── check_agreement
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── clean_text
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── coding_master
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── compare_and_contrast
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── convert_to_markdown
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_5_sentence_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_academic_paper
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_ai_jobs_analysis
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_aphorisms
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_art_prompt
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_better_frame
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_coding_project
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_command
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_cyber_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_design_document
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_diy
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_formal_email
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_git_diff_commit
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_graph_from_input
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_hormozi_offer
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_idea_compass
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_investigation_visualization
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_keynote
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_logo
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_markmap_visualization
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_mermaid_visualization
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_mermaid_visualization_for_github
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_micro_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_network_threat_landscape
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_newsletter_entry
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_npc
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_pattern
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_prd
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_prediction_block
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_quiz
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_reading_plan
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_recursive_outline
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_report_finding
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_rpg_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_security_update
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_show_intro
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_sigma_rules
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_story_explanation
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_stride_threat_model
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_summary
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_tags
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_threat_scenarios
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_ttrc_graph
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_ttrc_narrative
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_upgrade_pack
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_user_story
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── create_video_chapters
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── create_visualization
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── dialog_with_socrates
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── enrich_blog_post
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── explain_code
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── explain_docs
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── explain_math
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── explain_project
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── explain_terms
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── export_data_as_csv
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_algorithm_update_recommendations
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_article_wisdom
│               │   │   │   │   │   ├── dmiessler
│               │   │   │   │   │   │   └── extract_wisdom-1.0.0
│               │   │   │   │   │   │       ├── system.md
│               │   │   │   │   │   │       └── user.md
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_book_ideas
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_book_recommendations
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_business_ideas
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_controversial_ideas
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_core_message
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_ctf_writeup
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_domains
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_extraordinary_claims
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_ideas
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_insights
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_insights_dm
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_instructions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_jokes
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_latest_video
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_main_idea
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_most_redeeming_thing
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_patterns
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_poc
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_predictions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_primary_problem
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_primary_solution
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_product_features
│               │   │   │   │   │   ├── dmiessler
│               │   │   │   │   │   │   └── extract_wisdom-1.0.0
│               │   │   │   │   │   │       ├── system.md
│               │   │   │   │   │   │       └── user.md
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_questions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_recipe
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_recommendations
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_references
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_skills
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_song_meaning
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_sponsors
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_videoid
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── extract_wisdom
│               │   │   │   │   │   ├── dmiessler
│               │   │   │   │   │   │   └── extract_wisdom-1.0.0
│               │   │   │   │   │   │       ├── system.md
│               │   │   │   │   │   │       └── user.md
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_wisdom_agents
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_wisdom_dm
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── extract_wisdom_nometa
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── find_hidden_message
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── find_logical_fallacies
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── get_wow_per_minute
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── get_youtube_rss
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── humanize
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_dsrp_distinctions
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_dsrp_perspectives
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_dsrp_relationships
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_dsrp_systems
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── identify_job_stories
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── improve_academic_writing
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── improve_prompt
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── improve_report_finding
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── improve_writing
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── judge_output
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── label_and_rate
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── loaded
│               │   │   │   │   ├── md_callout
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── official_pattern_template
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── pattern_explanations.md
│               │   │   │   │   ├── prepare_7s_strategy
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── provide_guidance
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── rate_ai_response
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── rate_ai_result
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── rate_content
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── rate_value
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── raw_query
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── raycast
│               │   │   │   │   │   ├── capture_thinkers_work
│               │   │   │   │   │   ├── create_story_explanation
│               │   │   │   │   │   ├── extract_primary_problem
│               │   │   │   │   │   ├── extract_wisdom
│               │   │   │   │   │   └── yt
│               │   │   │   │   ├── recommend_artists
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── recommend_pipeline_upgrades
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── recommend_talkpanel_topics
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── refine_design_document
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── review_design
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── sanitize_broken_html_to_markdown
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── show_fabric_options_markmap
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── solve_with_cot
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── stringify
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── suggest_pattern
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize
│               │   │   │   │   │   ├── dmiessler
│               │   │   │   │   │   │   └── summarize
│               │   │   │   │   │   │       ├── system.md
│               │   │   │   │   │   │       └── user.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_debate
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_git_changes
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_git_diff
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_lecture
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_legislation
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_meeting
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_micro
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_newsletter
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_paper
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_prompt
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── summarize_pull-requests
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── summarize_rpg_session
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_analyze_challenge_handling
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_check_metrics
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_create_h3_career
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_create_opening_sentences
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_describe_life_outlook
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_extract_intro_sentences
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_extract_panel_topics
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_find_blindspots
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_find_negative_thinking
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_find_neglected_goals
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_give_encouragement
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_red_team_thinking
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_threat_model_plans
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_visualize_mission_goals_projects
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── t_year_in_review
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── to_flashcards
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── transcribe_minutes
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── translate
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── tweet
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_essay
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_hackerone_report
│               │   │   │   │   │   ├── README.md
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_latex
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_micro_essay
│               │   │   │   │   │   └── system.md
│               │   │   │   │   ├── write_nuclei_template_rule
│               │   │   │   │   │   ├── system.md
│               │   │   │   │   │   └── user.md
│               │   │   │   │   ├── write_pull-request
│               │   │   │   │   │   └── system.md
│               │   │   │   │   └── write_semgrep_rule
│               │   │   │   │       ├── system.md
│               │   │   │   │       └── user.md
│               │   │   │   └── routines
│               │   │   │       ├── list.py
│               │   │   │       ├── run.py
│               │   │   │       └── show.py
│               │   │   ├── guided_conversation
│               │   │   │   ├── __init__.py
│               │   │   │   ├── agenda.py
│               │   │   │   ├── artifact_helpers.py
│               │   │   │   ├── chat_completions
│               │   │   │   │   ├── fix_agenda_error.py
│               │   │   │   │   ├── fix_artifact_error.py
│               │   │   │   │   ├── generate_agenda.py
│               │   │   │   │   ├── generate_artifact_updates.py
│               │   │   │   │   ├── generate_final_artifact.py
│               │   │   │   │   └── generate_message.py
│               │   │   │   ├── conversation_guides
│               │   │   │   │   ├── __init__.py
│               │   │   │   │   ├── acrostic_poem.py
│               │   │   │   │   ├── er_triage.py
│               │   │   │   │   ├── interview.py
│               │   │   │   │   └── patient_intake.py
│               │   │   │   ├── guide.py
│               │   │   │   ├── guided_conversation_skill.py
│               │   │   │   ├── logging.py
│               │   │   │   ├── message.py
│               │   │   │   ├── resources.py
│               │   │   │   ├── routines
│               │   │   │   │   └── guided_conversation.py
│               │   │   │   └── tests
│               │   │   │       ├── conftest.py
│               │   │   │       ├── test_artifact_helpers.py
│               │   │   │       ├── test_generate_agenda.py
│               │   │   │       ├── test_generate_artifact_updates.py
│               │   │   │       ├── test_generate_final_artifact.py
│               │   │   │       └── test_resource.py
│               │   │   ├── meta
│               │   │   │   ├── __init__.py
│               │   │   │   ├── meta_skill.py
│               │   │   │   ├── README.md
│               │   │   │   └── routines
│               │   │   │       └── generate_routine.py
│               │   │   ├── posix
│               │   │   │   ├── __init__.py
│               │   │   │   ├── posix_skill.py
│               │   │   │   ├── routines
│               │   │   │   │   ├── append_file.py
│               │   │   │   │   ├── cd.py
│               │   │   │   │   ├── ls.py
│               │   │   │   │   ├── make_home_dir.py
│               │   │   │   │   ├── mkdir.py
│               │   │   │   │   ├── mv.py
│               │   │   │   │   ├── pwd.py
│               │   │   │   │   ├── read_file.py
│               │   │   │   │   ├── rm.py
│               │   │   │   │   ├── touch.py
│               │   │   │   │   └── write_file.py
│               │   │   │   └── sandbox_shell.py
│               │   │   ├── README.md
│               │   │   ├── research
│               │   │   │   ├── __init__.py
│               │   │   │   ├── README.md
│               │   │   │   ├── research_skill.py
│               │   │   │   └── routines
│               │   │   │       ├── answer_question_about_content.py
│               │   │   │       ├── evaluate_answer.py
│               │   │   │       ├── generate_research_plan.py
│               │   │   │       ├── generate_search_query.py
│               │   │   │       ├── update_research_plan.py
│               │   │   │       ├── web_research.py
│               │   │   │       └── web_search.py
│               │   │   ├── research2
│               │   │   │   ├── __init__.py
│               │   │   │   ├── README.md
│               │   │   │   ├── research_skill.py
│               │   │   │   └── routines
│               │   │   │       ├── facts.py
│               │   │   │       ├── make_final_report.py
│               │   │   │       ├── research.py
│               │   │   │       ├── search_plan.py
│               │   │   │       ├── search.py
│               │   │   │       └── visit_pages.py
│               │   │   └── web_research
│               │   │       ├── __init__.py
│               │   │       ├── README.md
│               │   │       ├── research_skill.py
│               │   │       └── routines
│               │   │           ├── facts.py
│               │   │           ├── make_final_report.py
│               │   │           ├── research.py
│               │   │           ├── search_plan.py
│               │   │           ├── search.py
│               │   │           └── visit_pages.py
│               │   ├── tests
│               │   │   ├── test_common_skill.py
│               │   │   ├── test_integration.py
│               │   │   ├── test_routine_stack.py
│               │   │   ├── tst_skill
│               │   │   │   ├── __init__.py
│               │   │   │   └── routines
│               │   │   │       ├── __init__.py
│               │   │   │       └── a_routine.py
│               │   │   └── utilities
│               │   │       ├── test_find_template_vars.py
│               │   │       ├── test_make_arg_set.py
│               │   │       ├── test_paramspec.py
│               │   │       ├── test_parse_command_string.py
│               │   │       └── test_to_string.py
│               │   ├── types.py
│               │   ├── usage.py
│               │   └── utilities.py
│               └── uv.lock
├── LICENSE
├── Makefile
├── mcp-servers
│   ├── ai-assist-content
│   │   ├── .vscode
│   │   │   └── settings.json
│   │   ├── mcp-example-brave-search.md
│   │   ├── mcp-fastmcp-typescript-README.md
│   │   ├── mcp-llms-full.txt
│   │   ├── mcp-metadata-tips.md
│   │   ├── mcp-python-sdk-README.md
│   │   ├── mcp-typescript-sdk-README.md
│   │   ├── pydanticai-documentation.md
│   │   ├── pydanticai-example-question-graph.md
│   │   ├── pydanticai-example-weather.md
│   │   ├── pydanticai-tutorial.md
│   │   └── README.md
│   ├── Makefile
│   ├── mcp-server-bing-search
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_bing_search
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── prompts
│   │   │   │   ├── __init__.py
│   │   │   │   ├── clean_website.py
│   │   │   │   └── filter_links.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   ├── tools.py
│   │   │   ├── types.py
│   │   │   ├── utils.py
│   │   │   └── web
│   │   │       ├── __init__.py
│   │   │       ├── get_content.py
│   │   │       ├── llm_processing.py
│   │   │       ├── process_website.py
│   │   │       └── search_bing.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   └── test_tools.py
│   │   └── uv.lock
│   ├── mcp-server-bundle
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_bundle
│   │   │   ├── __init__.py
│   │   │   └── main.py
│   │   ├── pyinstaller.spec
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-filesystem
│   │   ├── .env.example
│   │   ├── .github
│   │   │   └── workflows
│   │   │       └── ci.yml
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_filesystem
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── server.py
│   │   │   └── start.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   └── test_filesystem.py
│   │   └── uv.lock
│   ├── mcp-server-filesystem-edit
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── data
│   │   │   ├── attachments
│   │   │   │   ├── Daily Game Ideas.txt
│   │   │   │   ├── Frontend Framework Proposal.txt
│   │   │   │   ├── ReDoodle.txt
│   │   │   │   └── Research Template.tex
│   │   │   ├── test_cases.yaml
│   │   │   └── transcripts
│   │   │       ├── transcript_research_simple.md
│   │   │       ├── transcript_Startup_Idea_1_202503031513.md
│   │   │       ├── transcript_Startup_Idea_2_202503031659.md
│   │   │       └── transcript_Web_Frontends_202502281551.md
│   │   ├── Makefile
│   │   ├── mcp_server_filesystem_edit
│   │   │   ├── __init__.py
│   │   │   ├── app_handling
│   │   │   │   ├── __init__.py
│   │   │   │   ├── excel.py
│   │   │   │   ├── miktex.py
│   │   │   │   ├── office_common.py
│   │   │   │   ├── powerpoint.py
│   │   │   │   └── word.py
│   │   │   ├── config.py
│   │   │   ├── evals
│   │   │   │   ├── __init__.py
│   │   │   │   ├── common.py
│   │   │   │   ├── run_comments.py
│   │   │   │   ├── run_edit.py
│   │   │   │   └── run_ppt_edit.py
│   │   │   ├── prompts
│   │   │   │   ├── __init__.py
│   │   │   │   ├── add_comments.py
│   │   │   │   ├── analyze_comments.py
│   │   │   │   ├── latex_edit.py
│   │   │   │   ├── markdown_draft.py
│   │   │   │   ├── markdown_edit.py
│   │   │   │   └── powerpoint_edit.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   ├── tools
│   │   │   │   ├── __init__.py
│   │   │   │   ├── add_comments.py
│   │   │   │   ├── edit_adapters
│   │   │   │   │   ├── __init__.py
│   │   │   │   │   ├── common.py
│   │   │   │   │   ├── latex.py
│   │   │   │   │   └── markdown.py
│   │   │   │   ├── edit.py
│   │   │   │   └── helpers.py
│   │   │   └── types.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   ├── app_handling
│   │   │   │   ├── test_excel.py
│   │   │   │   ├── test_miktext.py
│   │   │   │   ├── test_office_common.py
│   │   │   │   ├── test_powerpoint.py
│   │   │   │   └── test_word.py
│   │   │   ├── conftest.py
│   │   │   └── tools
│   │   │       └── edit_adapters
│   │   │           ├── test_latex.py
│   │   │           └── test_markdown.py
│   │   └── uv.lock
│   ├── mcp-server-fusion
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── AddInIcon.svg
│   │   ├── config.py
│   │   ├── FusionMCPServerAddIn.manifest
│   │   ├── FusionMCPServerAddIn.py
│   │   ├── mcp_server_fusion
│   │   │   ├── __init__.py
│   │   │   ├── fusion_mcp_server.py
│   │   │   ├── fusion_utils
│   │   │   │   ├── __init__.py
│   │   │   │   ├── event_utils.py
│   │   │   │   ├── general_utils.py
│   │   │   │   └── tool_utils.py
│   │   │   ├── mcp_tools
│   │   │   │   ├── __init__.py
│   │   │   │   ├── fusion_3d_operation.py
│   │   │   │   ├── fusion_geometry.py
│   │   │   │   ├── fusion_pattern.py
│   │   │   │   └── fusion_sketch.py
│   │   │   └── vendor
│   │   │       └── README.md
│   │   ├── README.md
│   │   └── requirements.txt
│   ├── mcp-server-giphy
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── giphy_search.py
│   │   │   ├── sampling.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   └── utils.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-memory-user-bio
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_memory_user_bio
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── server.py
│   │   │   └── start.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-memory-whiteboard
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_memory_whiteboard
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── server.py
│   │   │   └── start.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-office
│   │   ├── .env.example
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── build.sh
│   │   ├── data
│   │   │   ├── attachments
│   │   │   │   ├── Daily Game Ideas.txt
│   │   │   │   ├── Frontend Framework Proposal.txt
│   │   │   │   └── ReDoodle.txt
│   │   │   └── word
│   │   │       ├── test_cases.yaml
│   │   │       └── transcripts
│   │   │           ├── transcript_Startup_Idea_1_202503031513.md
│   │   │           ├── transcript_Startup_Idea_2_202503031659.md
│   │   │           └── transcript_Web_Frontends_202502281551.md
│   │   ├── Makefile
│   │   ├── mcp_server
│   │   │   ├── __init__.py
│   │   │   ├── app_interaction
│   │   │   │   ├── __init__.py
│   │   │   │   ├── excel_editor.py
│   │   │   │   ├── powerpoint_editor.py
│   │   │   │   └── word_editor.py
│   │   │   ├── config.py
│   │   │   ├── constants.py
│   │   │   ├── evals
│   │   │   │   ├── __init__.py
│   │   │   │   ├── common.py
│   │   │   │   ├── run_comment_analysis.py
│   │   │   │   ├── run_feedback.py
│   │   │   │   └── run_markdown_edit.py
│   │   │   ├── helpers.py
│   │   │   ├── markdown_edit
│   │   │   │   ├── __init__.py
│   │   │   │   ├── comment_analysis.py
│   │   │   │   ├── feedback_step.py
│   │   │   │   ├── markdown_edit.py
│   │   │   │   └── utils.py
│   │   │   ├── prompts
│   │   │   │   ├── __init__.py
│   │   │   │   ├── comment_analysis.py
│   │   │   │   ├── feedback.py
│   │   │   │   ├── markdown_draft.py
│   │   │   │   └── markdown_edit.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   └── types.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── tests
│   │   │   └── test_word_editor.py
│   │   └── uv.lock
│   ├── mcp-server-open-deep-research
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server
│   │   │   ├── __init__.py
│   │   │   ├── config.py
│   │   │   ├── libs
│   │   │   │   └── open_deep_research
│   │   │   │       ├── cookies.py
│   │   │   │       ├── mdconvert.py
│   │   │   │       ├── run_agents.py
│   │   │   │       ├── text_inspector_tool.py
│   │   │   │       ├── text_web_browser.py
│   │   │   │       └── visual_qa.py
│   │   │   ├── open_deep_research.py
│   │   │   ├── server.py
│   │   │   └── start.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   └── uv.lock
│   ├── mcp-server-open-deep-research-clone
│   │   ├── .env.example
│   │   ├── .gitignore
│   │   ├── .vscode
│   │   │   ├── launch.json
│   │   │   └── settings.json
│   │   ├── Makefile
│   │   ├── mcp_server_open_deep_research_clone
│   │   │   ├── __init__.py
│   │   │   ├── azure_openai.py
│   │   │   ├── config.py
│   │   │   ├── logging.py
│   │   │   ├── sampling.py
│   │   │   ├── server.py
│   │   │   ├── start.py
│   │   │   ├── utils.py
│   │   │   └── web_research.py
│   │   ├── pyproject.toml
│   │   ├── README.md
│   │   ├── test
│   │   │   └── test_open_deep_research_clone.py
│   │   └── uv.lock
│   ├── mcp-server-template
│   │   ├── .taplo.toml
│   │   ├── .vscode
│   │   │   └── settings.json
│   │   ├── copier.yml
│   │   ├── README.md
│   │   └── template
│   │       └── {{ project_slug }}
│   │           ├── .env.example.jinja
│   │           ├── .gitignore
│   │           ├── .vscode
│   │           │   ├── launch.json.jinja
│   │           │   └── settings.json
│   │           ├── {{ module_name }}
│   │           │   ├── __init__.py
│   │           │   ├── config.py.jinja
│   │           │   ├── server.py.jinja
│   │           │   └── start.py.jinja
│   │           ├── Makefile.jinja
│   │           ├── pyproject.toml.jinja
│   │           └── README.md.jinja
│   ├── mcp-server-vscode
│   │   ├── .eslintrc.cjs
│   │   ├── .gitignore
│   │   ├── .npmrc
│   │   ├── .vscode
│   │   │   ├── extensions.json
│   │   │   ├── launch.json
│   │   │   ├── settings.json
│   │   │   └── tasks.json
│   │   ├── .vscode-test.mjs
│   │   ├── .vscodeignore
│   │   ├── ASSISTANT_BOOTSTRAP.md
│   │   ├── eslint.config.mjs
│   │   ├── images
│   │   │   └── icon.png
│   │   ├── LICENSE
│   │   ├── Makefile
│   │   ├── out
│   │   │   ├── extension.d.ts
│   │   │   ├── extension.js
│   │   │   ├── test
│   │   │   │   ├── extension.test.d.ts
│   │   │   │   └── extension.test.js
│   │   │   ├── tools
│   │   │   │   ├── code_checker.d.ts
│   │   │   │   ├── code_checker.js
│   │   │   │   ├── debug_tools.d.ts
│   │   │   │   ├── debug_tools.js
│   │   │   │   ├── focus_editor.d.ts
│   │   │   │   ├── focus_editor.js
│   │   │   │   ├── search_symbol.d.ts
│   │   │   │   └── search_symbol.js
│   │   │   └── utils
│   │   │       ├── port.d.ts
│   │   │       └── port.js
│   │   ├── package.json
│   │   ├── pnpm-lock.yaml
│   │   ├── prettier.config.cjs
│   │   ├── README.md
│   │   ├── src
│   │   │   ├── extension.d.ts
│   │   │   ├── extension.ts
│   │   │   ├── test
│   │   │   │   ├── extension.test.d.ts
│   │   │   │   └── extension.test.ts
│   │   │   ├── tools
│   │   │   │   ├── code_checker.d.ts
│   │   │   │   ├── code_checker.ts
│   │   │   │   ├── debug_tools.d.ts
│   │   │   │   ├── debug_tools.ts
│   │   │   │   ├── focus_editor.d.ts
│   │   │   │   ├── focus_editor.ts
│   │   │   │   ├── search_symbol.d.ts
│   │   │   │   └── search_symbol.ts
│   │   │   └── utils
│   │   │       ├── port.d.ts
│   │   │       └── port.ts
│   │   ├── tsconfig.json
│   │   ├── tsconfig.tsbuildinfo
│   │   ├── vsc-extension-quickstart.md
│   │   └── webpack.config.js
│   └── mcp-server-web-research
│       ├── .env.example
│       ├── .gitignore
│       ├── .vscode
│       │   ├── launch.json
│       │   └── settings.json
│       ├── Makefile
│       ├── mcp_server_web_research
│       │   ├── __init__.py
│       │   ├── azure_openai.py
│       │   ├── config.py
│       │   ├── logging.py
│       │   ├── sampling.py
│       │   ├── server.py
│       │   ├── start.py
│       │   ├── utils.py
│       │   └── web_research.py
│       ├── pyproject.toml
│       ├── README.md
│       ├── test
│       │   └── test_web_research.py
│       └── uv.lock
├── README.md
├── RESPONSIBLE_AI_FAQ.md
├── ruff.toml
├── SECURITY.md
├── semantic-workbench.code-workspace
├── SUPPORT.md
├── tools
│   ├── build_ai_context_files.py
│   ├── collect_files.py
│   ├── docker
│   │   ├── azure_website_sshd.conf
│   │   ├── docker-entrypoint.sh
│   │   ├── Dockerfile.assistant
│   │   └── Dockerfile.mcp-server
│   ├── makefiles
│   │   ├── docker-assistant.mk
│   │   ├── docker-mcp-server.mk
│   │   ├── docker.mk
│   │   ├── python.mk
│   │   ├── recursive.mk
│   │   └── shell.mk
│   ├── reset-service-data.ps1
│   ├── reset-service-data.sh
│   ├── run-app.ps1
│   ├── run-app.sh
│   ├── run-canonical-agent.ps1
│   ├── run-canonical-agent.sh
│   ├── run-dotnet-examples-with-aspire.sh
│   ├── run-python-example1.sh
│   ├── run-python-example2.ps1
│   ├── run-python-example2.sh
│   ├── run-service.ps1
│   ├── run-service.sh
│   ├── run-workbench-chatbot.ps1
│   └── run-workbench-chatbot.sh
├── workbench-app
│   ├── .dockerignore
│   ├── .env.example
│   ├── .eslintrc.cjs
│   ├── .gitignore
│   ├── .vscode
│   │   ├── launch.json
│   │   └── settings.json
│   ├── docker-entrypoint.sh
│   ├── Dockerfile
│   ├── docs
│   │   ├── APP_DEV_GUIDE.md
│   │   ├── MESSAGE_METADATA.md
│   │   ├── MESSAGE_TYPES.md
│   │   ├── README.md
│   │   └── STATE_INSPECTORS.md
│   ├── index.html
│   ├── Makefile
│   ├── nginx.conf
│   ├── package.json
│   ├── pnpm-lock.yaml
│   ├── prettier.config.cjs
│   ├── public
│   │   └── assets
│   │       ├── background-1-upscaled.jpg
│   │       ├── background-1-upscaled.png
│   │       ├── background-1.jpg
│   │       ├── background-1.png
│   │       ├── background-2.jpg
│   │       ├── background-2.png
│   │       ├── experimental-feature.jpg
│   │       ├── favicon.svg
│   │       ├── workflow-designer-1.jpg
│   │       ├── workflow-designer-outlets.jpg
│   │       ├── workflow-designer-states.jpg
│   │       └── workflow-designer-transitions.jpg
│   ├── README.md
│   ├── run.sh
│   ├── src
│   │   ├── components
│   │   │   ├── App
│   │   │   │   ├── AppFooter.tsx
│   │   │   │   ├── AppHeader.tsx
│   │   │   │   ├── AppMenu.tsx
│   │   │   │   ├── AppView.tsx
│   │   │   │   ├── CodeLabel.tsx
│   │   │   │   ├── CommandButton.tsx
│   │   │   │   ├── ConfirmLeave.tsx
│   │   │   │   ├── ContentExport.tsx
│   │   │   │   ├── ContentImport.tsx
│   │   │   │   ├── CopyButton.tsx
│   │   │   │   ├── DialogControl.tsx
│   │   │   │   ├── DynamicIframe.tsx
│   │   │   │   ├── ErrorListFromAppState.tsx
│   │   │   │   ├── ErrorMessageBar.tsx
│   │   │   │   ├── ExperimentalNotice.tsx
│   │   │   │   ├── FormWidgets
│   │   │   │   │   ├── BaseModelEditorWidget.tsx
│   │   │   │   │   ├── CustomizedArrayFieldTemplate.tsx
│   │   │   │   │   ├── CustomizedFieldTemplate.tsx
│   │   │   │   │   ├── CustomizedObjectFieldTemplate.tsx
│   │   │   │   │   └── InspectableWidget.tsx
│   │   │   │   ├── LabelWithDescription.tsx
│   │   │   │   ├── Loading.tsx
│   │   │   │   ├── MenuItemControl.tsx
│   │   │   │   ├── MiniControl.tsx
│   │   │   │   ├── MyAssistantServiceRegistrations.tsx
│   │   │   │   ├── MyItemsManager.tsx
│   │   │   │   ├── OverflowMenu.tsx
│   │   │   │   ├── PresenceMotionList.tsx
│   │   │   │   ├── ProfileSettings.tsx
│   │   │   │   └── TooltipWrapper.tsx
│   │   │   ├── Assistants
│   │   │   │   ├── ApplyConfigButton.tsx
│   │   │   │   ├── AssistantAdd.tsx
│   │   │   │   ├── AssistantConfigExportButton.tsx
│   │   │   │   ├── AssistantConfigImportButton.tsx
│   │   │   │   ├── AssistantConfiguration.tsx
│   │   │   │   ├── AssistantConfigure.tsx
│   │   │   │   ├── AssistantCreate.tsx
│   │   │   │   ├── AssistantDelete.tsx
│   │   │   │   ├── AssistantDuplicate.tsx
│   │   │   │   ├── AssistantExport.tsx
│   │   │   │   ├── AssistantImport.tsx
│   │   │   │   ├── AssistantRemove.tsx
│   │   │   │   ├── AssistantRename.tsx
│   │   │   │   ├── AssistantServiceInfo.tsx
│   │   │   │   ├── AssistantServiceMetadata.tsx
│   │   │   │   └── MyAssistants.tsx
│   │   │   ├── AssistantServiceRegistrations
│   │   │   │   ├── AssistantServiceRegistrationApiKey.tsx
│   │   │   │   ├── AssistantServiceRegistrationApiKeyReset.tsx
│   │   │   │   ├── AssistantServiceRegistrationCreate.tsx
│   │   │   │   └── AssistantServiceRegistrationRemove.tsx
│   │   │   ├── Conversations
│   │   │   │   ├── Canvas
│   │   │   │   │   ├── AssistantCanvas.tsx
│   │   │   │   │   ├── AssistantCanvasList.tsx
│   │   │   │   │   ├── AssistantInspector.tsx
│   │   │   │   │   ├── AssistantInspectorList.tsx
│   │   │   │   │   └── ConversationCanvas.tsx
│   │   │   │   ├── ChatInputPlugins
│   │   │   │   │   ├── ClearEditorPlugin.tsx
│   │   │   │   │   ├── LexicalMenu.ts
│   │   │   │   │   ├── ParticipantMentionsPlugin.tsx
│   │   │   │   │   ├── TypeaheadMenuPlugin.css
│   │   │   │   │   └── TypeaheadMenuPlugin.tsx
│   │   │   │   ├── ContentRenderers
│   │   │   │   │   ├── CodeContentRenderer.tsx
│   │   │   │   │   ├── ContentListRenderer.tsx
│   │   │   │   │   ├── ContentRenderer.tsx
│   │   │   │   │   ├── DiffRenderer.tsx
│   │   │   │   │   ├── HtmlContentRenderer.tsx
│   │   │   │   │   ├── JsonSchemaContentRenderer.tsx
│   │   │   │   │   ├── MarkdownContentRenderer.tsx
│   │   │   │   │   ├── MarkdownEditorRenderer.tsx
│   │   │   │   │   ├── MermaidContentRenderer.tsx
│   │   │   │   │   ├── MusicABCContentRenderer.css
│   │   │   │   │   └── MusicABCContentRenderer.tsx
│   │   │   │   ├── ContextWindow.tsx
│   │   │   │   ├── ConversationCreate.tsx
│   │   │   │   ├── ConversationDuplicate.tsx
│   │   │   │   ├── ConversationExport.tsx
│   │   │   │   ├── ConversationFileIcon.tsx
│   │   │   │   ├── ConversationRemove.tsx
│   │   │   │   ├── ConversationRename.tsx
│   │   │   │   ├── ConversationShare.tsx
│   │   │   │   ├── ConversationShareCreate.tsx
│   │   │   │   ├── ConversationShareList.tsx
│   │   │   │   ├── ConversationShareView.tsx
│   │   │   │   ├── ConversationsImport.tsx
│   │   │   │   ├── ConversationTranscript.tsx
│   │   │   │   ├── DebugInspector.tsx
│   │   │   │   ├── FileItem.tsx
│   │   │   │   ├── FileList.tsx
│   │   │   │   ├── InputAttachmentList.tsx
│   │   │   │   ├── InputOptionsControl.tsx
│   │   │   │   ├── InteractHistory.tsx
│   │   │   │   ├── InteractInput.tsx
│   │   │   │   ├── Message
│   │   │   │   │   ├── AttachmentSection.tsx
│   │   │   │   │   ├── ContentRenderer.tsx
│   │   │   │   │   ├── ContentSafetyNotice.tsx
│   │   │   │   │   ├── InteractMessage.tsx
│   │   │   │   │   ├── MessageActions.tsx
│   │   │   │   │   ├── MessageBase.tsx
│   │   │   │   │   ├── MessageBody.tsx
│   │   │   │   │   ├── MessageContent.tsx
│   │   │   │   │   ├── MessageFooter.tsx
│   │   │   │   │   ├── MessageHeader.tsx
│   │   │   │   │   ├── NotificationAccordion.tsx
│   │   │   │   │   └── ToolResultMessage.tsx
│   │   │   │   ├── MessageDelete.tsx
│   │   │   │   ├── MessageLink.tsx
│   │   │   │   ├── MyConversations.tsx
│   │   │   │   ├── MyShares.tsx
│   │   │   │   ├── ParticipantAvatar.tsx
│   │   │   │   ├── ParticipantAvatarGroup.tsx
│   │   │   │   ├── ParticipantItem.tsx
│   │   │   │   ├── ParticipantList.tsx
│   │   │   │   ├── ParticipantStatus.tsx
│   │   │   │   ├── RewindConversation.tsx
│   │   │   │   ├── ShareRemove.tsx
│   │   │   │   ├── SpeechButton.tsx
│   │   │   │   └── ToolCalls.tsx
│   │   │   └── FrontDoor
│   │   │       ├── Chat
│   │   │       │   ├── AssistantDrawer.tsx
│   │   │       │   ├── CanvasDrawer.tsx
│   │   │       │   ├── Chat.tsx
│   │   │       │   ├── ChatCanvas.tsx
│   │   │       │   ├── ChatControls.tsx
│   │   │       │   └── ConversationDrawer.tsx
│   │   │       ├── Controls
│   │   │       │   ├── AssistantCard.tsx
│   │   │       │   ├── AssistantSelector.tsx
│   │   │       │   ├── AssistantServiceSelector.tsx
│   │   │       │   ├── ConversationItem.tsx
│   │   │       │   ├── ConversationList.tsx
│   │   │       │   ├── ConversationListOptions.tsx
│   │   │       │   ├── NewConversationButton.tsx
│   │   │       │   ├── NewConversationForm.tsx
│   │   │       │   └── SiteMenuButton.tsx
│   │   │       ├── GlobalContent.tsx
│   │   │       └── MainContent.tsx
│   │   ├── Constants.ts
│   │   ├── global.d.ts
│   │   ├── index.css
│   │   ├── libs
│   │   │   ├── AppStorage.ts
│   │   │   ├── AuthHelper.ts
│   │   │   ├── EventSubscriptionManager.ts
│   │   │   ├── Theme.ts
│   │   │   ├── useAssistantCapabilities.ts
│   │   │   ├── useChatCanvasController.ts
│   │   │   ├── useConversationEvents.ts
│   │   │   ├── useConversationUtility.ts
│   │   │   ├── useCreateConversation.ts
│   │   │   ├── useDebugComponentLifecycle.ts
│   │   │   ├── useDragAndDrop.ts
│   │   │   ├── useEnvironment.ts
│   │   │   ├── useExportUtility.ts
│   │   │   ├── useHistoryUtility.ts
│   │   │   ├── useKeySequence.ts
│   │   │   ├── useMediaQuery.ts
│   │   │   ├── useMicrosoftGraph.ts
│   │   │   ├── useNotify.tsx
│   │   │   ├── useParticipantUtility.tsx
│   │   │   ├── useSiteUtility.ts
│   │   │   ├── useWorkbenchEventSource.ts
│   │   │   ├── useWorkbenchService.ts
│   │   │   └── Utility.ts
│   │   ├── main.tsx
│   │   ├── models
│   │   │   ├── Assistant.ts
│   │   │   ├── AssistantCapability.ts
│   │   │   ├── AssistantServiceInfo.ts
│   │   │   ├── AssistantServiceRegistration.ts
│   │   │   ├── Config.ts
│   │   │   ├── Conversation.ts
│   │   │   ├── ConversationFile.ts
│   │   │   ├── ConversationMessage.ts
│   │   │   ├── ConversationMessageDebug.ts
│   │   │   ├── ConversationParticipant.ts
│   │   │   ├── ConversationShare.ts
│   │   │   ├── ConversationShareRedemption.ts
│   │   │   ├── ConversationState.ts
│   │   │   ├── ConversationStateDescription.ts
│   │   │   ├── ServiceEnvironment.ts
│   │   │   └── User.ts
│   │   ├── redux
│   │   │   ├── app
│   │   │   │   ├── hooks.ts
│   │   │   │   ├── rtkQueryErrorLogger.ts
│   │   │   │   └── store.ts
│   │   │   └── features
│   │   │       ├── app
│   │   │       │   ├── appSlice.ts
│   │   │       │   └── AppState.ts
│   │   │       ├── chatCanvas
│   │   │       │   ├── chatCanvasSlice.ts
│   │   │       │   └── ChatCanvasState.ts
│   │   │       ├── localUser
│   │   │       │   ├── localUserSlice.ts
│   │   │       │   └── LocalUserState.ts
│   │   │       └── settings
│   │   │           ├── settingsSlice.ts
│   │   │           └── SettingsState.ts
│   │   ├── Root.tsx
│   │   ├── routes
│   │   │   ├── AcceptTerms.tsx
│   │   │   ├── AssistantEditor.tsx
│   │   │   ├── AssistantServiceRegistrationEditor.tsx
│   │   │   ├── Dashboard.tsx
│   │   │   ├── ErrorPage.tsx
│   │   │   ├── FrontDoor.tsx
│   │   │   ├── Login.tsx
│   │   │   ├── Settings.tsx
│   │   │   ├── ShareRedeem.tsx
│   │   │   └── Shares.tsx
│   │   ├── services
│   │   │   └── workbench
│   │   │       ├── assistant.ts
│   │   │       ├── assistantService.ts
│   │   │       ├── conversation.ts
│   │   │       ├── file.ts
│   │   │       ├── index.ts
│   │   │       ├── participant.ts
│   │   │       ├── share.ts
│   │   │       ├── state.ts
│   │   │       └── workbench.ts
│   │   └── vite-env.d.ts
│   ├── tools
│   │   └── filtered-ts-prune.cjs
│   ├── tsconfig.json
│   └── vite.config.ts
└── workbench-service
    ├── .env.example
    ├── .vscode
    │   ├── extensions.json
    │   ├── launch.json
    │   └── settings.json
    ├── alembic.ini
    ├── devdb
    │   ├── docker-compose.yaml
    │   └── postgresql-init.sh
    ├── Dockerfile
    ├── Makefile
    ├── migrations
    │   ├── env.py
    │   ├── README
    │   ├── script.py.mako
    │   └── versions
    │       ├── 2024_09_19_000000_69dcda481c14_init.py
    │       ├── 2024_09_19_190029_dffb1d7e219a_file_version_filename.py
    │       ├── 2024_09_20_204130_b29524775484_share.py
    │       ├── 2024_10_30_231536_039bec8edc33_index_message_type.py
    │       ├── 2024_11_04_204029_5149c7fb5a32_conversationmessagedebug.py
    │       ├── 2024_11_05_015124_245baf258e11_double_check_debugs.py
    │       ├── 2024_11_25_191056_a106de176394_drop_workflow.py
    │       ├── 2025_03_19_140136_aaaf792d4d72_set_user_title_set.py
    │       ├── 2025_03_21_153250_3763629295ad_add_assistant_template_id.py
    │       ├── 2025_05_19_163613_b2f86e981885_delete_context_transfer_assistants.py
    │       └── 2025_06_18_174328_503c739152f3_delete_knowlege_transfer_assistants.py
    ├── pyproject.toml
    ├── README.md
    ├── semantic_workbench_service
    │   ├── __init__.py
    │   ├── api.py
    │   ├── assistant_api_key.py
    │   ├── auth.py
    │   ├── azure_speech.py
    │   ├── config.py
    │   ├── controller
    │   │   ├── __init__.py
    │   │   ├── assistant_service_client_pool.py
    │   │   ├── assistant_service_registration.py
    │   │   ├── assistant.py
    │   │   ├── conversation_share.py
    │   │   ├── conversation.py
    │   │   ├── convert.py
    │   │   ├── exceptions.py
    │   │   ├── export_import.py
    │   │   ├── file.py
    │   │   ├── participant.py
    │   │   └── user.py
    │   ├── db.py
    │   ├── event.py
    │   ├── files.py
    │   ├── logging_config.py
    │   ├── middleware.py
    │   ├── query.py
    │   ├── service_user_principals.py
    │   ├── service.py
    │   └── start.py
    ├── tests
    │   ├── __init__.py
    │   ├── conftest.py
    │   ├── docker-compose.yaml
    │   ├── test_assistant_api_key.py
    │   ├── test_files.py
    │   ├── test_integration.py
    │   ├── test_middleware.py
    │   ├── test_migrations.py
    │   ├── test_workbench_service.py
    │   └── types.py
    └── uv.lock
```

# Files

--------------------------------------------------------------------------------
/assistants/project-assistant/assistant/agentic/respond.py:
--------------------------------------------------------------------------------

```python
  1 | import time
  2 | from textwrap import dedent
  3 | from typing import Any, ClassVar
  4 | 
  5 | import openai_client
  6 | from assistant_extensions.attachments import AttachmentsExtension
  7 | from openai import BaseModel
  8 | from openai.types.chat import (
  9 |     ChatCompletionAssistantMessageParam,
 10 |     ChatCompletionMessageParam,
 11 |     ChatCompletionSystemMessageParam,
 12 |     ChatCompletionUserMessageParam,
 13 | )
 14 | from openai_client import num_tokens_from_messages
 15 | from openai_client.completion import assistant_message_from_completion
 16 | from openai_client.errors import CompletionError
 17 | from openai_client.tools import complete_with_tool_calls
 18 | from pydantic import ConfigDict, Field
 19 | from semantic_workbench_api_model.workbench_model import (
 20 |     ConversationMessage,
 21 |     ConversationParticipantList,
 22 |     MessageType,
 23 |     NewConversationMessage,
 24 | )
 25 | from semantic_workbench_assistant.assistant_app import (
 26 |     ConversationContext,
 27 | )
 28 | 
 29 | from assistant.config import assistant_config
 30 | from assistant.data import ConversationRole
 31 | from assistant.domain.conversation_preferences_manager import (
 32 |     ConversationPreferencesManager,
 33 | )
 34 | from assistant.domain.share_manager import ShareManager
 35 | from assistant.logging import logger
 36 | from assistant.prompt_utils import (
 37 |     ContextSection,
 38 |     ContextStrategy,
 39 |     DataContext,
 40 |     Instructions,
 41 |     Prompt,
 42 |     TokenBudget,
 43 |     add_context_to_prompt,
 44 | )
 45 | from assistant.tools import ShareTools
 46 | from assistant.utils import load_text_include
 47 | 
 48 | SILENCE_TOKEN = "{{SILENCE}}"
 49 | 
 50 | 
 51 | def format_message(participants: ConversationParticipantList, message: ConversationMessage) -> str:
 52 |     """Consistent formatter that includes the participant name for multi-participant and name references"""
 53 |     conversation_participant = next(
 54 |         (participant for participant in participants.participants if participant.id == message.sender.participant_id),
 55 |         None,
 56 |     )
 57 |     participant_name = conversation_participant.name if conversation_participant else "unknown"
 58 |     message_datetime = message.timestamp.strftime("%Y-%m-%d %H:%M:%S")
 59 |     return f"[{participant_name} - {message_datetime}]: {message.content}"
 60 | 
 61 | 
 62 | class CoordinatorOutput(BaseModel):
 63 |     """
 64 |     Attributes:
 65 |         response: The response from the assistant.
 66 |     """
 67 | 
 68 |     response: str = Field(
 69 |         description="The response from the assistant. The response should not duplicate information from the excerpt but may refer to it.",  # noqa: E501
 70 |     )
 71 | 
 72 |     model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
 73 | 
 74 | 
 75 | class TeamOutput(BaseModel):
 76 |     """
 77 |     Attributes:
 78 |         citations: A list of citations from which the response is generated. There should always be at least one citation, but it can be empty if the assistant has no relevant information to cite.
 79 |         excerpt: A verbatim excerpt from one of the cited works that illustrates why this response was given. It should have enough context to get a good idea of what's in that part of the cited work. DO NOT excerpt from CONVERSATION or DIGEST, only from attachments. If there is no relevant excerpt, this will be None. If there is special formatting in the excerpt, remove it as the excerpt will be displayed in quotes in a chat message and should not contain any formatting that would not be supported in a chat message (e.g. markdown).
 80 |         next_step_suggestion: Suggest more areas to explore using content from the knowledge digest to ensure your conversation covers all of the relevant information.
 81 |     """  # noqa: E501
 82 | 
 83 |     citations: list[str] = Field(
 84 |         description="A list of citations from which the response is generated. There should always be at least one citation, but it can be empty if the assistant has no relevant information to cite.",  # noqa: E501
 85 |     )
 86 |     excerpt: str | None = Field(
 87 |         description="A verbatim excerpt from one of the cited works that illustrates why this response was given. It should have enough context to get a good idea of what's in that part of the cited work. DO NOT excerpt from CONVERSATION or KNOWLEDGE_DIGEST, only from attachments. If there is no relevant excerpt, this will be None. If there is special formatting in the excerpt, remove it as the excerpt will be displayed in quotes in a chat message and should not contain any formatting that would not be supported in a chat message (e.g. markdown).",  # noqa: E501
 88 |     )
 89 |     response: str = Field(
 90 |         description="The response from the assistant. The response should not duplicate information from the excerpt but may refer to it.",  # noqa: E501
 91 |     )
 92 |     next_step_suggestion: str = Field(
 93 |         description="Suggest more areas to explore using content from the knowledge digest to ensure your conversation covers all of the relevant information. For example: 'Would you like to explore ... next?'.",  # noqa: E501
 94 |     )
 95 | 
 96 |     model_config: ClassVar[ConfigDict] = ConfigDict(extra="forbid")
 97 | 
 98 | 
 99 | async def respond_to_conversation(
100 |     context: ConversationContext,
101 |     new_message: ConversationMessage,
102 |     attachments_extension: AttachmentsExtension,
103 |     metadata: dict[str, Any],
104 |     user_information_requests: list[str] | None = None,
105 | ) -> ChatCompletionAssistantMessageParam | None:
106 |     """
107 |     Respond to a conversation message.
108 |     """
109 |     if "debug" not in metadata:
110 |         metadata["debug"] = {}
111 | 
112 |     config = await assistant_config.get(context.assistant)
113 |     model = config.request_config.openai_model
114 |     role = await ShareManager.get_conversation_role(context)
115 |     metadata["debug"]["role"] = role
116 |     token_budget = TokenBudget(config.request_config.max_tokens)
117 | 
118 |     ##
119 |     ## INSTRUCTIONS
120 |     ##
121 | 
122 |     # Add role-specific instructions.
123 |     if role == ConversationRole.COORDINATOR:
124 |         role_specific_instructions = config.prompt_config.coordinator_instructions
125 |     else:
126 |         role_specific_instructions = config.prompt_config.team_instructions
127 |     instructions = Instructions(role_specific_instructions)
128 | 
129 |     # Add knowledge digest instructions.
130 |     instructions.add_subsection(
131 |         Instructions(
132 |             load_text_include("knowledge_digest_instructions.txt"),
133 |             "Assistant's Knowledge Digest",
134 |         )
135 |     )
136 | 
137 |     # If this is a multi-participant conversation, add a note about the participants.
138 |     participants = await context.get_participants(include_inactive=True)
139 |     if len(participants.participants) > 2:
140 |         participant_text = (
141 |             "\n\n"
142 |             f"There are {len(participants.participants)} participants in the conversation,"
143 |             " including you as the assistant and the following users:"
144 |             + ",".join([
145 |                 f' "{participant.name}"'
146 |                 for participant in participants.participants
147 |                 if participant.id != context.assistant.id
148 |             ])
149 |             + "\n\nYou do not need to respond to every message. Do not respond if the last thing said was a closing"
150 |             " statement such as 'bye' or 'goodbye', or just a general acknowledgement like 'ok' or 'thanks'. Do not"
151 |             f' respond as another user in the conversation, only as "{context.assistant.name}".'
152 |             " Sometimes the other users need to talk amongst themselves and that is ok. If the conversation seems to"
153 |             f' be directed at you or the general audience, go ahead and respond.\n\nSay "{SILENCE_TOKEN}" to skip'
154 |             " your turn."
155 |         )
156 |         instructions.add_subsection(Instructions(participant_text, "Multi-participant conversation instructions"))
157 | 
158 |     # Add conversation preferences instructions.
159 |     communication_style = await ConversationPreferencesManager.get_preferred_communication_style(context)
160 |     instructions.add_subsection(Instructions(communication_style, "Preferred Communication Style"))
161 | 
162 |     prompt = Prompt(
163 |         instructions=instructions,
164 |         context_strategy=ContextStrategy.MULTI,
165 |     )
166 |     if role == ConversationRole.TEAM:
167 |         prompt.output_format = "Respond as JSON with your response in the `response` field and all citations in the `citations` field. In the `next_step_suggestion` field, suggest more areas to explore using content from the assistant whiteboard to ensure your conversation covers all of the relevant information."  # noqa: E501
168 | 
169 |     ##
170 |     ## CONTEXT
171 |     ##
172 | 
173 |     sections = [
174 |         ContextSection.KNOWLEDGE_INFO,
175 |         ContextSection.KNOWLEDGE_BRIEF,
176 |         ContextSection.TARGET_AUDIENCE,
177 |         # ContextSection.LEARNING_OBJECTIVES,
178 |         ContextSection.KNOWLEDGE_DIGEST,
179 |         ContextSection.INFORMATION_REQUESTS,
180 |         # ContextSection.SUGGESTED_NEXT_ACTIONS,
181 |         ContextSection.ATTACHMENTS,
182 |         ContextSection.TASKS,
183 |     ]
184 |     if role == ConversationRole.TEAM:
185 |         sections.append(ContextSection.COORDINATOR_CONVERSATION)
186 | 
187 |     await add_context_to_prompt(
188 |         prompt,
189 |         context=context,
190 |         role=role,
191 |         model=model,
192 |         token_limit=config.request_config.max_tokens,
193 |         attachments_extension=attachments_extension,
194 |         attachments_config=config.attachments_config,
195 |         attachments_in_system_message=False,
196 |         include=sections,
197 |     )
198 | 
199 |     user_information_requests_data = "- ".join(user_information_requests) if user_information_requests else "None"
200 |     prompt.contexts.append(
201 |         DataContext(
202 |             "Information Needed from the User",
203 |             user_information_requests_data,
204 |         )
205 |     )
206 | 
207 |     # Calculate token count for all prompt so far.
208 |     completion_messages = prompt.messages()
209 |     token_budget.add(
210 |         num_tokens_from_messages(
211 |             model=model,
212 |             messages=completion_messages,
213 |         )
214 |     )
215 | 
216 |     ###
217 |     ### USER MESSAGE
218 |     ###
219 | 
220 |     if new_message.sender.participant_id == context.assistant.id:
221 |         user_message: ChatCompletionMessageParam = ChatCompletionAssistantMessageParam(
222 |             role="assistant",
223 |             content=format_message(participants, new_message),
224 |         )
225 |     else:
226 |         user_message: ChatCompletionMessageParam = ChatCompletionUserMessageParam(
227 |             role="user",
228 |             content=format_message(participants, new_message),
229 |         )
230 | 
231 |     token_budget.add(
232 |         num_tokens_from_messages(
233 |             model=model,
234 |             messages=[user_message],
235 |         )
236 |     )
237 | 
238 |     ###
239 |     ### HISTORY MESSAGES
240 |     ###
241 | 
242 |     history_messages: list[ChatCompletionMessageParam] = []
243 |     before_message_id = new_message.id
244 |     history_token_budget = TokenBudget(token_budget.remaining())
245 | 
246 |     # Fetch messages from the workbench in batches that will fit our token budget.
247 |     under_budget = True
248 |     while under_budget:
249 |         # Get a batch of messages
250 |         messages_response = await context.get_messages(
251 |             before=before_message_id,
252 |             limit=100,
253 |             message_types=[MessageType.chat],
254 |         )
255 |         messages_list = messages_response.messages
256 |         if not messages_list or len(messages_list) == 0:
257 |             break
258 |         before_message_id = messages_list[0].id
259 | 
260 |         for msg in reversed(messages_list):
261 |             if msg.sender.participant_id == context.assistant.id:
262 |                 # For assistant messages, include help suggestions as part of the message content
263 |                 message_content = format_message(participants, msg)
264 |                 if msg.metadata and "help" in msg.metadata:
265 |                     message_content += f"\n\n[Next step?: {msg.metadata['help']}]"
266 | 
267 |                 current_message = ChatCompletionAssistantMessageParam(
268 |                     role="assistant",
269 |                     content=message_content,
270 |                 )
271 |             else:
272 |                 current_message = ChatCompletionUserMessageParam(
273 |                     role="user",
274 |                     content=format_message(participants, msg),
275 |                 )
276 | 
277 |             current_message_tokens = num_tokens_from_messages(
278 |                 model=model,
279 |                 messages=[current_message],
280 |             )
281 | 
282 |             if history_token_budget.fits(current_message_tokens):
283 |                 history_messages = [current_message, *history_messages]
284 |                 history_token_budget.add(current_message_tokens)
285 |             else:
286 |                 under_budget = False
287 |                 break
288 | 
289 |         if not under_budget:
290 |             break
291 | 
292 |     # Add all chat messages.
293 |     completion_messages.extend(history_messages)
294 |     completion_messages.append(user_message)
295 | 
296 |     # Add a system message to indicate attachments are a part of the new message.
297 |     if new_message.filenames and len(new_message.filenames) > 0:
298 |         attachment_message = ChatCompletionSystemMessageParam(
299 |             role="system",
300 |             content=f"Attachment(s): {', '.join(new_message.filenames)}",
301 |         )
302 |         completion_messages.append(attachment_message)
303 |         token_budget.add(
304 |             num_tokens_from_messages(
305 |                 model=model,
306 |                 messages=[attachment_message],
307 |             )
308 |         )
309 | 
310 |     ##
311 |     ## Final token count check
312 |     ##
313 |     token_counts = {"total": token_budget.used, "max": token_budget.budget}
314 |     metadata["debug"]["token_usage"] = token_counts  # For debug.
315 |     metadata["token_counts"] = token_counts  # For footer.
316 |     if token_budget.remaining() < 0:
317 |         raise ValueError(
318 |             f"You've exceeded the token limit of {token_budget.budget} in this conversation "
319 |             f"({token_budget.used}). Try removing some attachments."
320 |         )
321 | 
322 |     ##
323 |     ## MAKE THE LLM CALL
324 |     ##
325 | 
326 |     content = ""
327 |     async with openai_client.create_client(config.service_config) as client:
328 |         try:
329 |             completion_args = {
330 |                 "messages": completion_messages,
331 |                 "model": model,
332 |                 "max_tokens": config.request_config.response_tokens,
333 |                 "response_format": CoordinatorOutput if role == ConversationRole.COORDINATOR else TeamOutput,
334 |             }
335 | 
336 |             share_tools = ShareTools(context)
337 |             tool_functions = (
338 |                 share_tools.conversationalist_tools()
339 |                 if role == ConversationRole.COORDINATOR
340 |                 else share_tools.team_tools()
341 |             )
342 |             response_start_time = time.time()
343 |             completion_response, _ = await complete_with_tool_calls(
344 |                 async_client=client,
345 |                 completion_args=completion_args,
346 |                 tool_functions=tool_functions,
347 |                 metadata=metadata["debug"],
348 |                 max_tool_call_rounds=32,
349 |             )
350 |             response_end_time = time.time()
351 |             footer_items = []
352 | 
353 |             # Add the token usage message to the footer items
354 |             if completion_response:
355 |                 response_tokens = completion_response.usage.completion_tokens if completion_response.usage else 0
356 |                 request_tokens = token_budget.used
357 |                 footer_items.append(
358 |                     get_token_usage_message(
359 |                         max_tokens=config.request_config.max_tokens,
360 |                         total_tokens=request_tokens + response_tokens,
361 |                         request_tokens=request_tokens,
362 |                         completion_tokens=response_tokens,
363 |                     )
364 |                 )
365 | 
366 |                 await context.update_conversation(
367 |                     metadata={
368 |                         "token_counts": {
369 |                             "total": request_tokens + response_tokens,
370 |                             "max": config.request_config.max_tokens,
371 |                         }
372 |                     }
373 |                 )
374 | 
375 |             footer_items.append(get_response_duration_message(response_end_time - response_start_time))
376 |             metadata["footer_items"] = footer_items
377 |             return assistant_message_from_completion(completion_response) if completion_response else None
378 | 
379 |         except CompletionError as e:
380 |             logger.exception(f"Exception occurred calling OpenAI chat completion: {e}")
381 |             metadata["debug"]["error"] = str(e)
382 |             if isinstance(e.body, dict) and "message" in e.body:
383 |                 content = e.body.get("message", e.message)
384 |             elif e.message:
385 |                 content = e.message
386 |             else:
387 |                 content = "An error occurred while processing your request."
388 |             await context.send_messages(
389 |                 NewConversationMessage(
390 |                     content=content,
391 |                     message_type=MessageType.notice,
392 |                     metadata=metadata,
393 |                 )
394 |             )
395 |             return
396 | 
397 | 
398 | def get_formatted_token_count(tokens: int) -> str:
399 |     # if less than 1k, return the number of tokens
400 |     # if greater than or equal to 1k, return the number of tokens in k
401 |     # use 1 decimal place for k
402 |     # drop the decimal place if the number of tokens in k is a whole number
403 |     if tokens < 1000:
404 |         return str(tokens)
405 |     else:
406 |         tokens_in_k = tokens / 1000
407 |         if tokens_in_k.is_integer():
408 |             return f"{int(tokens_in_k)}k"
409 |         else:
410 |             return f"{tokens_in_k:.1f}k"
411 | 
412 | 
413 | def get_token_usage_message(
414 |     max_tokens: int,
415 |     total_tokens: int,
416 |     request_tokens: int,
417 |     completion_tokens: int,
418 | ) -> str:
419 |     """
420 |     Generate a display friendly message for the token usage, to be added to the footer items.
421 |     """
422 | 
423 |     return dedent(f"""
424 |         Tokens used: {get_formatted_token_count(total_tokens)}
425 |         ({get_formatted_token_count(request_tokens)} in / {get_formatted_token_count(completion_tokens)} out)
426 |         of {get_formatted_token_count(max_tokens)} ({int(total_tokens / max_tokens * 100)}%)
427 |     """).strip()
428 | 
429 | 
430 | def get_response_duration_message(response_duration: float) -> str:
431 |     """
432 |     Generate a display friendly message for the response duration, to be added to the footer items.
433 |     """
434 | 
435 |     return f"Response time: {response_duration:.2f} seconds"
436 | 
```

--------------------------------------------------------------------------------
/assistants/project-assistant/assistant/prompt_utils.py:
--------------------------------------------------------------------------------

```python
  1 | import json
  2 | from dataclasses import dataclass, field
  3 | from enum import Enum
  4 | from typing import Protocol
  5 | 
  6 | import openai_client
  7 | from assistant_extensions.attachments import (
  8 |     AttachmentsConfigModel,
  9 |     AttachmentsExtension,
 10 | )
 11 | from openai.types.chat import (
 12 |     ChatCompletionMessageParam,
 13 | )
 14 | from pydantic import BaseModel, Field
 15 | from semantic_workbench_assistant.assistant_app import (
 16 |     ConversationContext,
 17 | )
 18 | 
 19 | from assistant.data import (
 20 |     ConversationRole,
 21 |     CoordinatorConversationMessage,
 22 |     RequestStatus,
 23 | )
 24 | from assistant.domain.learning_objectives_manager import LearningObjectivesManager
 25 | from assistant.domain.share_manager import ShareManager
 26 | from assistant.domain.tasks_manager import TasksManager
 27 | from assistant.ui_tabs.common import get_priority_emoji, get_status_emoji
 28 | 
 29 | 
 30 | def create_system_message(content: str, delimiter: str | None = None) -> ChatCompletionMessageParam:
 31 |     if delimiter:
 32 |         delimiter = delimiter.strip().upper().replace(" ", "_")
 33 |         content = f"<{delimiter}>\n{content}\n</{delimiter}>"
 34 | 
 35 |     message: ChatCompletionMessageParam = {
 36 |         "role": "system",
 37 |         "content": content,
 38 |     }
 39 |     return message
 40 | 
 41 | 
 42 | class Instructions:
 43 |     """
 44 |     A class to represent a section of a prompt.
 45 |     """
 46 | 
 47 |     def __init__(
 48 |         self,
 49 |         content: str,
 50 |         title: str | None = None,
 51 |     ) -> None:
 52 |         self.title = title
 53 |         self.content = content
 54 |         self.level = 0
 55 |         self.subsections: list[Instructions] = []
 56 | 
 57 |     def add_subsection(self, subsection: "Instructions") -> None:
 58 |         """
 59 |         Add a subsection to the prompt section.
 60 |         """
 61 |         subsection.level = self.level + 1
 62 |         self.subsections.append(subsection)
 63 | 
 64 |     def __str__(self) -> str:
 65 |         s = ""
 66 |         if self.title:
 67 |             hashes = "#" * (self.level + 1)
 68 |             s += f"{hashes} {self.title}\n\n"
 69 |         s += self.content
 70 |         if self.subsections:
 71 |             s += "\n\n" + "\n\n".join(str(subsection) for subsection in self.subsections)
 72 | 
 73 |         return s
 74 | 
 75 | 
 76 | class Context(Protocol):
 77 |     def message(self) -> ChatCompletionMessageParam:
 78 |         raise NotImplementedError
 79 | 
 80 |     def content(self) -> str:
 81 |         raise NotImplementedError
 82 | 
 83 |     def name(self) -> str:
 84 |         raise NotImplementedError
 85 | 
 86 | 
 87 | class ChatCompletionMessageContext(Context):
 88 |     def __init__(self, message: ChatCompletionMessageParam, name: str | None) -> None:
 89 |         self._message = message
 90 |         self._name = name or "Attachment"
 91 | 
 92 |     def message(self) -> ChatCompletionMessageParam:
 93 |         return self._message
 94 | 
 95 |     def content(self) -> str:
 96 |         return f"<{self._name}>\n{self._message.get('content')}\n</{self._name}>"
 97 | 
 98 |     def name(self) -> str:
 99 |         return self._name
100 | 
101 | 
102 | class DataContext(Context):
103 |     def __init__(self, name: str, data: str, description: str | None = None) -> None:
104 |         self._name = name
105 |         self.description = description
106 |         self.data = data
107 | 
108 |     def message(self) -> ChatCompletionMessageParam:
109 |         return create_system_message(self.content(), self._name)
110 | 
111 |     def content(self) -> str:
112 |         s = self.data
113 |         if self.description:
114 |             s = f"{self.description}\n\n'''\n{self.data}\n'''"
115 |         return s
116 | 
117 |     def name(self) -> str:
118 |         return self._name
119 | 
120 | 
121 | class ContextStrategy(Enum):
122 |     SINGLE = "single"  # Put all context chunks in a single message.
123 |     MULTI = "multi"  # Put each context chunk in its own message.
124 | 
125 | 
126 | @dataclass
127 | class Prompt:
128 |     instructions: Instructions
129 |     output_format: str | None = None
130 |     reasoning_steps: str | None = None
131 |     examples: str | None = None
132 |     contexts: list[Context] = field(default_factory=list)
133 |     context_strategy: ContextStrategy = ContextStrategy.SINGLE
134 |     final_instructions: str | None = None
135 | 
136 |     def messages(self) -> list[ChatCompletionMessageParam]:
137 |         parts = [
138 |             str(self.instructions),
139 |         ]
140 |         if self.reasoning_steps:
141 |             parts.append("# Reasoning Steps")
142 |             parts.append(self.reasoning_steps)
143 |         if self.output_format:
144 |             parts.append("# Output Format")
145 |             parts.append(self.output_format)
146 |         if self.examples:
147 |             parts.append("# Examples")
148 |             parts.append(self.examples)
149 |         if self.contexts and self.context_strategy == ContextStrategy.SINGLE:
150 |             parts.append("# Context")
151 |             for context in self.contexts:
152 |                 parts.append(f"## {context.name()}")
153 |                 parts.append(context.content())
154 |         s = "\n\n".join(parts)
155 |         if self.final_instructions:
156 |             s += "\n\n" + self.final_instructions
157 | 
158 |         messages = [
159 |             create_system_message(s),
160 |         ]
161 | 
162 |         if self.contexts and self.context_strategy == ContextStrategy.MULTI:
163 |             for context in self.contexts:
164 |                 messages.append(context.message())
165 | 
166 |         return messages
167 | 
168 | 
169 | class TokenBudget:
170 |     def __init__(self, budget: int) -> None:
171 |         self.budget = budget
172 |         self.used = 0
173 | 
174 |     def add(self, tokens: int) -> None:
175 |         self.used += tokens
176 | 
177 |     def remaining(self) -> int:
178 |         return self.budget - self.used
179 | 
180 |     def is_under_budget(self) -> bool:
181 |         return self.remaining() > 0
182 | 
183 |     def is_over_budget(self) -> bool:
184 |         return self.remaining() < 0
185 | 
186 |     def fits(self, tokens: int) -> bool:
187 |         return self.remaining() >= tokens
188 | 
189 | 
190 | class ContextSection(Enum):
191 |     """
192 |     Enum to represent different sections of the conversation context.
193 |     """
194 | 
195 |     KNOWLEDGE_INFO = "knowledge_info"
196 |     KNOWLEDGE_BRIEF = "knowledge_brief"
197 |     TARGET_AUDIENCE = "target_audience"
198 |     LEARNING_OBJECTIVES = "learning_objectives"
199 |     KNOWLEDGE_DIGEST = "knowledge_digest"
200 |     INFORMATION_REQUESTS = "information_requests"
201 |     SUGGESTED_NEXT_ACTIONS = "suggested_next_actions"
202 |     COORDINATOR_CONVERSATION = "coordinator_conversation"
203 |     ATTACHMENTS = "attachments"
204 |     TASKS = "tasks"
205 | 
206 | 
207 | async def add_context_to_prompt(
208 |     prompt: Prompt,
209 |     context: ConversationContext,
210 |     role: ConversationRole,
211 |     model: str,
212 |     token_limit: int,
213 |     attachments_extension: AttachmentsExtension | None = None,
214 |     attachments_config: AttachmentsConfigModel | None = None,
215 |     attachments_in_system_message: bool = False,
216 |     include: list[ContextSection] | None = None,
217 | ) -> None:
218 |     if include is None:
219 |         return
220 | 
221 |     share = await ShareManager.get_share(context)
222 | 
223 |     if ContextSection.TASKS in include:
224 |         tasks = await TasksManager.get_tasks(context)
225 |         if tasks:
226 |             tasks_data = json.dumps([task.model_dump() for task in tasks])
227 |             prompt.contexts.append(
228 |                 DataContext(
229 |                     "Task List",
230 |                     tasks_data,
231 |                 )
232 |             )
233 | 
234 |     if ContextSection.KNOWLEDGE_INFO in include:
235 |         share_info_text = share.model_dump_json(
236 |             indent=2,
237 |             exclude={
238 |                 "brief",
239 |                 "learning_objectives",
240 |                 "audience_takeaways",
241 |                 "preferred_communication_style",
242 |                 "digest",
243 |                 "next_learning_actions",
244 |                 "requests",
245 |                 "tasks",
246 |                 "log",
247 |             },
248 |         )
249 |         prompt.contexts.append(DataContext("Knowledge Share Info", share_info_text))
250 | 
251 |     if ContextSection.KNOWLEDGE_BRIEF in include and share and share.brief:
252 |         brief_text = ""
253 |         brief_text = f"**Title:** {share.brief.title}\n**Description:** {share.brief.content}"
254 |         prompt.contexts.append(
255 |             DataContext(
256 |                 "Knowledge Brief",
257 |                 brief_text,
258 |             )
259 |         )
260 | 
261 |     if ContextSection.TARGET_AUDIENCE in include and role == ConversationRole.COORDINATOR and share:
262 |         if share.audience:
263 |             audience_context = share.audience
264 | 
265 |             if share.audience_takeaways:
266 |                 audience_context += "\n\n**Intended takeaways for this audience:**\n"
267 |                 audience_context += "\n".join(f"- {takeaway}" for takeaway in share.audience_takeaways)
268 |             else:
269 |                 audience_context += "\n\n**Note:** No specific takeaways defined for this audience. Please define them to help guide the knowledge transfer process."  # noqa: E501
270 | 
271 |             if not share.is_intended_to_accomplish_outcomes:
272 |                 audience_context += "\n\n**Note:** This knowledge package is intended for general exploration, not specific learning outcomes."  # noqa: E501
273 |         else:
274 |             audience_context = "The intended audience for this knowledge transfer has not been defined yet. Please define it to help guide the knowledge transfer process."  # noqa: E501
275 | 
276 |         prompt.contexts.append(
277 |             DataContext(
278 |                 "Target Audience",
279 |                 audience_context,
280 |             )
281 |         )
282 | 
283 |     # Learning objectives
284 |     if ContextSection.LEARNING_OBJECTIVES in include and share and share.learning_objectives:
285 |         learning_objectives_text = ""
286 |         conversation_id = str(context.id)
287 | 
288 |         # Show progress based on role
289 |         if role == ConversationRole.COORDINATOR:
290 |             # Coordinator sees overall progress across all team members
291 |             achieved_overall, total_overall = LearningObjectivesManager.get_overall_completion(share)
292 |             learning_objectives_text += (
293 |                 f"Overall Progress: {achieved_overall}/{total_overall} outcomes achieved by team members\n\n"
294 |             )
295 |         else:
296 |             # Team member sees their personal progress
297 |             if conversation_id in share.team_conversations:
298 |                 achieved_personal, total_personal = LearningObjectivesManager.get_completion_for_conversation(
299 |                     share, conversation_id
300 |                 )
301 |                 progress_pct = int(achieved_personal / total_personal * 100) if total_personal > 0 else 0
302 |                 learning_objectives_text += (
303 |                     f"My Progress: {achieved_personal}/{total_personal} outcomes achieved ({progress_pct}%)\n\n"
304 |                 )
305 | 
306 |         learning_objectives = {}
307 |         for objective in share.learning_objectives:
308 |             learning_objectives[objective.id] = objective.model_dump()
309 |         learning_objectives_text = json.dumps(
310 |             learning_objectives,
311 |             indent=2,
312 |         )
313 | 
314 |         prompt.contexts.append(
315 |             DataContext(
316 |                 "Learning Objectives",
317 |                 learning_objectives_text,
318 |             )
319 |         )
320 | 
321 |     if ContextSection.KNOWLEDGE_DIGEST in include and share and share.digest and share.digest.content:
322 |         prompt.contexts.append(
323 |             DataContext(
324 |                 "Knowledge digest",
325 |                 share.digest.content,
326 |                 "The assistant-maintained knowledge digest.",
327 |             )
328 |         )
329 | 
330 |     if ContextSection.INFORMATION_REQUESTS in include and share:
331 |         all_requests = share.requests
332 |         if role == ConversationRole.COORDINATOR:
333 |             active_requests = [r for r in all_requests if r.status != RequestStatus.RESOLVED]
334 |             if active_requests:
335 |                 coordinator_requests = ""
336 |                 for req in active_requests[:10]:  # Limit to 10 for brevity
337 |                     priority_emoji = get_priority_emoji(req.priority)
338 |                     status_emoji = get_status_emoji(req.status)
339 |                     coordinator_requests = f"{priority_emoji} **{req.title}** {status_emoji}\n"
340 |                     coordinator_requests += f"   **Request ID:** `{req.request_id}`\n"
341 |                     coordinator_requests += f"   **Description:** {req.description}\n\n"
342 | 
343 |                 if len(active_requests) > 10:
344 |                     coordinator_requests += f"*...and {len(active_requests) - 10} more requests.*\n"
345 |             else:
346 |                 coordinator_requests = "No active information requests."
347 |             prompt.contexts.append(
348 |                 DataContext(
349 |                     "Information Requests",
350 |                     coordinator_requests,
351 |                 )
352 |             )
353 |         else:  # team role
354 |             information_requests_info = ""
355 |             my_requests = []
356 | 
357 |             # Filter for requests from this conversation that aren't resolved.
358 |             my_requests = [
359 |                 r for r in all_requests if r.conversation_id == str(context.id) and r.status != RequestStatus.RESOLVED
360 |             ]
361 | 
362 |             if my_requests:
363 |                 information_requests_info = ""
364 |                 for req in my_requests:
365 |                     information_requests_info += (
366 |                         f"- **{req.title}** (ID: `{req.request_id}`, Priority: {req.priority})\n"
367 |                     )
368 |             else:
369 |                 information_requests_info = "No active information requests."
370 | 
371 |             prompt.contexts.append(
372 |                 DataContext(
373 |                     "Information Requests",
374 |                     information_requests_info,
375 |                 )
376 |             )
377 | 
378 |     # if ContextSection.SUGGESTED_NEXT_ACTIONS in include and share and role == ConversationRole.COORDINATOR:
379 |     #     next_action_suggestion = await get_coordinator_next_action_suggestion(context)
380 |     #     if next_action_suggestion:
381 |     #         prompt.contexts.append(
382 |     #             DataContext(
383 |     #                 "Suggested Next Actions",
384 |     #                 next_action_suggestion,
385 |     #                 "Actions the coordinator should consider taking based on the current knowledge transfer state.",
386 |     #             )
387 |     #         )
388 | 
389 |     # Figure out the token budget so far.
390 |     token_budget = TokenBudget(token_limit)
391 |     token_budget.add(
392 |         openai_client.num_tokens_from_messages(
393 |             model=model,
394 |             messages=prompt.messages(),
395 |         )
396 |     )
397 | 
398 |     # Coordinator conversation
399 |     if ContextSection.COORDINATOR_CONVERSATION in include:
400 |         coordinator_conversation = await ShareManager.get_coordinator_conversation(context)
401 |         if coordinator_conversation:
402 |             # Limit messages to the configured max token count.
403 |             total_coordinator_conversation_tokens = 0
404 |             selected_coordinator_conversation_messages: list[CoordinatorConversationMessage] = []
405 |             for msg in reversed(coordinator_conversation.messages):
406 |                 tokens = openai_client.num_tokens_from_string(msg.model_dump_json(), model=model)
407 |                 if total_coordinator_conversation_tokens + tokens > token_limit:
408 |                     break
409 |                 selected_coordinator_conversation_messages.append(msg)
410 |                 total_coordinator_conversation_tokens += tokens
411 | 
412 |             class CoordinatorMessageList(BaseModel):
413 |                 messages: list[CoordinatorConversationMessage] = Field(default_factory=list)
414 | 
415 |             selected_coordinator_conversation_messages.reverse()
416 |             coordinator_message_list = CoordinatorMessageList(messages=selected_coordinator_conversation_messages)
417 |             coordinator_message_list_data = coordinator_message_list.model_dump_json()
418 | 
419 |             if attachments_in_system_message:
420 |                 prompt.contexts.append(
421 |                     DataContext(
422 |                         "Message History" if role == ConversationRole.COORDINATOR else "Coordinator Conversation",
423 |                         coordinator_message_list_data,
424 |                     )
425 |                 )
426 |             else:
427 |                 if role == ConversationRole.COORDINATOR:
428 |                     coordinator_message_list_data = (
429 |                         f"<MESSAGE_HISTORY>{coordinator_message_list_data}</MESSAGE_HISTORY>"
430 |                     )
431 |                 else:
432 |                     coordinator_message_list_data = (
433 |                         f"<COORDINATOR_CONVERSATION>{coordinator_message_list_data}</COORDINATOR_CONVERSATION>"
434 |                     )
435 |                 prompt.contexts.append(DataContext("Attachment", coordinator_message_list_data))
436 | 
437 |             # TODO: To get exact token count, we should add delimiters.
438 |             token_budget.add(
439 |                 openai_client.num_tokens_from_string(
440 |                     model=model,
441 |                     string=coordinator_message_list_data,
442 |                 )
443 |             )
444 | 
445 |     # Attachments
446 |     if ContextSection.ATTACHMENTS in include and share and attachments_config and attachments_extension:
447 |         # Generate the attachment messages.
448 |         # TODO: This will exceed the token limit if there are too many attachments.
449 | 
450 |         attachment_messages: list[ChatCompletionMessageParam] = openai_client.convert_from_completion_messages(
451 |             await attachments_extension.get_completion_messages_for_attachments(
452 |                 context,
453 |                 config=attachments_config,
454 |             )
455 |         )
456 | 
457 |         if attachments_in_system_message:
458 |             attachments_data = "\n\n".join(f"{msg['content']}" for msg in attachment_messages if "content" in msg)
459 |             prompt.contexts.append(
460 |                 DataContext(
461 |                     "Attachments",
462 |                     attachments_data,
463 |                     "The attachments provided by the user.",
464 |                 )
465 |             )
466 |             # TODO: To get exact token count, we should add delimiters.
467 |             token_budget.add(
468 |                 openai_client.num_tokens_from_string(
469 |                     model=model,
470 |                     string=attachments_data,
471 |                 )
472 |             )
473 | 
474 |         else:
475 |             for a in attachment_messages:
476 |                 prompt.contexts.append(
477 |                     ChatCompletionMessageContext(
478 |                         name="Attachment",
479 |                         message=a,
480 |                     )
481 |                 )
482 |             token_budget.add(
483 |                 openai_client.num_tokens_from_messages(
484 |                     model=model,
485 |                     messages=attachment_messages,
486 |                 )
487 |             )
488 | 
```

--------------------------------------------------------------------------------
/examples/python/python-02-simple-chatbot/assistant/chat.py:
--------------------------------------------------------------------------------

```python
  1 | # Copyright (c) Microsoft. All rights reserved.
  2 | 
  3 | # An example for building a simple chat assistant using the AssistantApp from
  4 | # the semantic-workbench-assistant package.
  5 | #
  6 | # This example demonstrates how to use the AssistantApp to create a chat assistant,
  7 | # to add additional configuration fields and UI schema for the configuration fields,
  8 | # and to handle conversation events to respond to messages in the conversation.
  9 | 
 10 | # region Required
 11 | #
 12 | # The code in this region demonstrates the minimal code required to create a chat assistant
 13 | # using the AssistantApp class from the semantic-workbench-assistant package. This code
 14 | # demonstrates how to create an AssistantApp instance, define the service ID, name, and
 15 | # description, and create the FastAPI app instance. Start here to build your own chat
 16 | # assistant using the AssistantApp class.
 17 | #
 18 | # The code that follows this region is optional and demonstrates how to add event handlers
 19 | # to respond to conversation events. You can use this code as a starting point for building
 20 | # your own chat assistant with additional functionality.
 21 | #
 22 | 
 23 | 
 24 | import logging
 25 | import re
 26 | from typing import Any
 27 | 
 28 | import deepmerge
 29 | import openai_client
 30 | import tiktoken
 31 | from content_safety.evaluators import CombinedContentSafetyEvaluator
 32 | from openai.types.chat import ChatCompletionMessageParam
 33 | from semantic_workbench_api_model.workbench_model import (
 34 |     ConversationEvent,
 35 |     ConversationMessage,
 36 |     MessageType,
 37 |     NewConversationMessage,
 38 |     UpdateParticipant,
 39 | )
 40 | from semantic_workbench_assistant.assistant_app import (
 41 |     AssistantApp,
 42 |     BaseModelAssistantConfig,
 43 |     ContentSafety,
 44 |     ContentSafetyEvaluator,
 45 |     ConversationContext,
 46 | )
 47 | 
 48 | from .config import AssistantConfigModel
 49 | 
 50 | logger = logging.getLogger(__name__)
 51 | 
 52 | #
 53 | # define the service ID, name, and description
 54 | #
 55 | 
 56 | # the service id to be registered in the workbench to identify the assistant
 57 | service_id = "python-02-simple-chatbot.workbench-explorer"
 58 | # the name of the assistant service, as it will appear in the workbench UI
 59 | service_name = "Python Example 02: Simple Chatbot"
 60 | # a description of the assistant service, as it will appear in the workbench UI
 61 | service_description = "A simple OpenAI chat assistant using the Semantic Workbench Assistant SDK."
 62 | 
 63 | #
 64 | # create the configuration provider, using the extended configuration model
 65 | #
 66 | assistant_config = BaseModelAssistantConfig(AssistantConfigModel)
 67 | 
 68 | 
 69 | # define the content safety evaluator factory
 70 | async def content_evaluator_factory(context: ConversationContext) -> ContentSafetyEvaluator:
 71 |     config = await assistant_config.get(context.assistant)
 72 |     return CombinedContentSafetyEvaluator(config.content_safety_config)
 73 | 
 74 | 
 75 | content_safety = ContentSafety(content_evaluator_factory)
 76 | 
 77 | 
 78 | # create the AssistantApp instance
 79 | assistant = AssistantApp(
 80 |     assistant_service_id=service_id,
 81 |     assistant_service_name=service_name,
 82 |     assistant_service_description=service_description,
 83 |     config_provider=assistant_config.provider,
 84 |     content_interceptor=content_safety,
 85 | )
 86 | 
 87 | #
 88 | # create the FastAPI app instance
 89 | #
 90 | app = assistant.fastapi_app()
 91 | 
 92 | 
 93 | # endregion
 94 | 
 95 | 
 96 | # region Optional
 97 | #
 98 | # Note: The code in this region is specific to this example and is not required for a basic assistant.
 99 | #
100 | # The AssistantApp class provides a set of decorators for adding event handlers to respond to conversation
101 | # events. In VS Code, typing "@assistant." (or the name of your AssistantApp instance) will show available
102 | # events and methods.
103 | #
104 | # See the semantic-workbench-assistant AssistantApp class for more information on available events and methods.
105 | # Examples:
106 | # - @assistant.events.conversation.on_created (event triggered when the assistant is added to a conversation)
107 | # - @assistant.events.conversation.participant.on_created (event triggered when a participant is added)
108 | # - @assistant.events.conversation.message.on_created (event triggered when a new message of any type is created)
109 | # - @assistant.events.conversation.message.chat.on_created (event triggered when a new chat message is created)
110 | #
111 | 
112 | 
113 | @assistant.events.conversation.message.chat.on_created
114 | async def on_message_created(
115 |     context: ConversationContext, event: ConversationEvent, message: ConversationMessage
116 | ) -> None:
117 |     """
118 |     Handle the event triggered when a new chat message is created in the conversation.
119 | 
120 |     **Note**
121 |     - This event handler is specific to chat messages.
122 |     - To handle other message types, you can add additional event handlers for those message types.
123 |       - @assistant.events.conversation.message.log.on_created
124 |       - @assistant.events.conversation.message.command.on_created
125 |       - ...additional message types
126 |     - To handle all message types, you can use the root event handler for all message types:
127 |       - @assistant.events.conversation.message.on_created
128 |     """
129 | 
130 |     # update the participant status to indicate the assistant is thinking
131 |     await context.update_participant_me(UpdateParticipant(status="thinking..."))
132 |     try:
133 |         # replace the following with your own logic for processing a message created event
134 |         await respond_to_conversation(
135 |             context,
136 |             message=message,
137 |             metadata={"debug": {"content_safety": event.data.get(content_safety.metadata_key, {})}},
138 |         )
139 |     finally:
140 |         # update the participant status to indicate the assistant is done thinking
141 |         await context.update_participant_me(UpdateParticipant(status=None))
142 | 
143 | 
144 | # Handle the event triggered when the assistant is added to a conversation.
145 | @assistant.events.conversation.on_created
146 | async def on_conversation_created(context: ConversationContext) -> None:
147 |     """
148 |     Handle the event triggered when the assistant is added to a conversation.
149 |     """
150 |     # replace the following with your own logic for processing a conversation created event
151 | 
152 |     # get the assistant's configuration
153 |     config = await assistant_config.get(context.assistant)
154 | 
155 |     # get the welcome message from the assistant's configuration
156 |     welcome_message = config.welcome_message
157 | 
158 |     # send the welcome message to the conversation
159 |     await context.send_messages(
160 |         NewConversationMessage(
161 |             content=welcome_message,
162 |             message_type=MessageType.chat,
163 |             metadata={"generated_content": False},
164 |         )
165 |     )
166 | 
167 | 
168 | # endregion
169 | 
170 | 
171 | # region Custom
172 | #
173 | # This code was added specifically for this example to demonstrate how to respond to conversation
174 | # messages using the OpenAI API. For your own assistant, you could replace this code with your own
175 | # logic for responding to conversation messages and add any additional functionality as needed.
176 | #
177 | 
178 | 
179 | # demonstrates how to respond to a conversation message using the OpenAI API.
180 | async def respond_to_conversation(
181 |     context: ConversationContext, message: ConversationMessage, metadata: dict[str, Any] = {}
182 | ) -> None:
183 |     """
184 |     Respond to a conversation message.
185 |     """
186 | 
187 |     # define the metadata key for any metadata created within this method
188 |     method_metadata_key = "respond_to_conversation"
189 | 
190 |     # get the assistant's configuration, supports overwriting defaults from environment variables
191 |     config = await assistant_config.get(context.assistant)
192 | 
193 |     # get the list of conversation participants
194 |     participants_response = await context.get_participants(include_inactive=True)
195 | 
196 |     # establish a token to be used by the AI model to indicate no response
197 |     silence_token = "{{SILENCE}}"
198 | 
199 |     # create a system message, start by adding the guardrails prompt
200 |     system_message_content = config.guardrails_prompt
201 | 
202 |     # add the instruction prompt and the assistant name
203 |     system_message_content += f'\n\n{config.instruction_prompt}\n\nYour name is "{context.assistant.name}".'
204 | 
205 |     # if this is a multi-participant conversation, add a note about the participants
206 |     if len(participants_response.participants) > 2:
207 |         system_message_content += (
208 |             "\n\n"
209 |             f"There are {len(participants_response.participants)} participants in the conversation,"
210 |             " including you as the assistant and the following users:"
211 |             + ",".join([
212 |                 f' "{participant.name}"'
213 |                 for participant in participants_response.participants
214 |                 if participant.id != context.assistant.id
215 |             ])
216 |             + "\n\nYou do not need to respond to every message. Do not respond if the last thing said was a closing"
217 |             " statement such as 'bye' or 'goodbye', or just a general acknowledgement like 'ok' or 'thanks'. Do not"
218 |             f' respond as another user in the conversation, only as "{context.assistant.name}".'
219 |             " Sometimes the other users need to talk amongst themselves and that is ok. If the conversation seems to"
220 |             f' be directed at you or the general audience, go ahead and respond.\n\nSay "{silence_token}" to skip'
221 |             " your turn."
222 |         )
223 | 
224 |     # create the completion messages for the AI model and add the system message
225 |     completion_messages: list[ChatCompletionMessageParam] = [
226 |         {
227 |             "role": "system",
228 |             "content": system_message_content,
229 |         }
230 |     ]
231 | 
232 |     # get the current token count and track the tokens used as messages are added
233 |     current_tokens = 0
234 |     # add the token count for the system message
235 |     current_tokens += get_token_count(system_message_content)
236 | 
237 |     # consistent formatter that includes the participant name for multi-participant and name references
238 |     def format_message(message: ConversationMessage) -> str:
239 |         # get the participant name for the message sender
240 |         conversation_participant = next(
241 |             (
242 |                 participant
243 |                 for participant in participants_response.participants
244 |                 if participant.id == message.sender.participant_id
245 |             ),
246 |             None,
247 |         )
248 |         participant_name = conversation_participant.name if conversation_participant else "unknown"
249 | 
250 |         # format the message content with the participant name and message timestamp
251 |         message_datetime = message.timestamp.strftime("%Y-%m-%d %H:%M:%S")
252 |         return f"[{participant_name} - {message_datetime}]: {message.content}"
253 | 
254 |     # get messages before the current message
255 |     messages_response = await context.get_messages(before=message.id)
256 |     messages = messages_response.messages + [message]
257 | 
258 |     # create a list of the recent chat history messages to send to the AI model
259 |     history_messages: list[ChatCompletionMessageParam] = []
260 |     # iterate over the messages in reverse order to get the most recent messages first
261 |     for message in reversed(messages):
262 |         # add the token count for the message and check if the token limit has been reached
263 |         message_tokens = get_token_count(format_message(message))
264 |         current_tokens += message_tokens
265 |         if current_tokens > config.request_config.max_tokens - config.request_config.response_tokens:
266 |             # if the token limit has been reached, stop adding messages
267 |             break
268 | 
269 |         # add the message to the history messages
270 |         if message.sender.participant_id == context.assistant.id:
271 |             # this is an assistant message
272 |             history_messages.append({
273 |                 "role": "assistant",
274 |                 "content": format_message(message),
275 |             })
276 |         else:
277 |             # this is a user message
278 |             history_messages.append({
279 |                 "role": "user",
280 |                 "content": format_message(message),
281 |             })
282 | 
283 |     # reverse the history messages to send the most recent messages first
284 |     history_messages.reverse()
285 | 
286 |     # add the history messages to the completion messages
287 |     completion_messages.extend(history_messages)
288 | 
289 |     # evaluate the content for safety
290 |     # disabled because the OpenAI and Azure OpenAI services already have content safety checks
291 |     # and we are more interested in running the generated responses through the content safety checks
292 |     # which are being handled by the content safety interceptor on the assistant
293 |     # this code is therefore included here for reference on how to call the content safety evaluator
294 |     # from within the assistant code
295 | 
296 |     # content_evaluator = await content_evaluator_factory(context)
297 |     # evaluation = await content_evaluator.evaluate([message.content for message in messages])
298 | 
299 |     # deepmerge.always_merger.merge(
300 |     #     metadata,
301 |     #     {
302 |     #         "debug": {
303 |     #             f"{assistant.content_interceptor.metadata_key}": {
304 |     #                 f"{method_metadata_key}": {
305 |     #                     "evaluation": evaluation.model_dump(),
306 |     #                 },
307 |     #             },
308 |     #         },
309 |     #     },
310 |     # )
311 | 
312 |     # if evaluation.result == ContentSafetyEvaluationResult.Fail:
313 |     #     # send a notice to the user that the content safety evaluation failed
314 |     #     deepmerge.always_merger.merge(
315 |     #         metadata,
316 |     #         {"generated_content": False},
317 |     #     )
318 |     #     await context.send_messages(
319 |     #         NewConversationMessage(
320 |     #             content=evaluation.note or "Content safety evaluation failed.",
321 |     #             message_type=MessageType.notice,
322 |     #             metadata=metadata,
323 |     #         )
324 |     #     )
325 |     #     return
326 | 
327 |     # generate a response from the AI model
328 |     async with openai_client.create_client(config.service_config, api_version="2024-06-01") as client:
329 |         try:
330 |             # call the OpenAI chat completion endpoint to get a response
331 |             completion = await client.chat.completions.create(
332 |                 messages=completion_messages,
333 |                 model=config.request_config.openai_model,
334 |                 max_tokens=config.request_config.response_tokens,
335 |             )
336 | 
337 |             # get the content from the completion response
338 |             content = completion.choices[0].message.content
339 | 
340 |             # merge the completion response into the passed in metadata
341 |             deepmerge.always_merger.merge(
342 |                 metadata,
343 |                 {
344 |                     "debug": {
345 |                         f"{method_metadata_key}": {
346 |                             "request": {
347 |                                 "model": config.request_config.openai_model,
348 |                                 "messages": completion_messages,
349 |                                 "max_tokens": config.request_config.response_tokens,
350 |                             },
351 |                             "response": completion.model_dump() if completion else "[no response from openai]",
352 |                         },
353 |                     }
354 |                 },
355 |             )
356 |         except Exception as e:
357 |             logger.exception(f"exception occurred calling openai chat completion: {e}")
358 |             # if there is an error, set the content to an error message
359 |             content = "An error occurred while calling the OpenAI API. Is it configured correctly?"
360 | 
361 |             # merge the error into the passed in metadata
362 |             deepmerge.always_merger.merge(
363 |                 metadata,
364 |                 {
365 |                     "debug": {
366 |                         f"{method_metadata_key}": {
367 |                             "request": {
368 |                                 "model": config.request_config.openai_model,
369 |                                 "messages": completion_messages,
370 |                             },
371 |                             "error": str(e),
372 |                         },
373 |                     }
374 |                 },
375 |             )
376 | 
377 |     # set the message type based on the content
378 |     message_type = MessageType.chat
379 | 
380 |     # various behaviors based on the content
381 |     if content:
382 |         # strip out the username from the response
383 |         if content.startswith("["):
384 |             content = re.sub(r"\[.*\]:\s", "", content)
385 | 
386 |         # check for the silence token, in case the model chooses not to respond
387 |         # model sometimes puts extra spaces in the response, so remove them
388 |         # when checking for the silence token
389 |         if content.replace(" ", "") == silence_token:
390 |             # normal behavior is to not respond if the model chooses to remain silent
391 |             # but we can override this behavior for debugging purposes via the assistant config
392 |             if config.enable_debug_output:
393 |                 # update the metadata to indicate the assistant chose to remain silent
394 |                 deepmerge.always_merger.merge(
395 |                     metadata,
396 |                     {
397 |                         "debug": {
398 |                             f"{method_metadata_key}": {
399 |                                 "silence_token": True,
400 |                             },
401 |                         },
402 |                         "attribution": "debug output",
403 |                         "generated_content": False,
404 |                     },
405 |                 )
406 |                 # send a notice to the user that the assistant chose to remain silent
407 |                 await context.send_messages(
408 |                     NewConversationMessage(
409 |                         message_type=MessageType.notice,
410 |                         content="[assistant chose to remain silent]",
411 |                         metadata=metadata,
412 |                     )
413 |                 )
414 |             return
415 | 
416 |         # override message type if content starts with "/", indicating a command response
417 |         if content.startswith("/"):
418 |             message_type = MessageType.command_response
419 | 
420 |     # send the response to the conversation
421 |     await context.send_messages(
422 |         NewConversationMessage(
423 |             content=content or "[no response from openai]",
424 |             message_type=message_type,
425 |             metadata=metadata,
426 |         )
427 |     )
428 | 
429 | 
430 | # this method is used to get the token count of a string.
431 | def get_token_count(string: str) -> int:
432 |     """
433 |     Get the token count of a string.
434 |     """
435 |     encoding = tiktoken.get_encoding("cl100k_base")
436 |     return len(encoding.encode(string))
437 | 
438 | 
439 | # endregion
440 | 
```
Page 90/145FirstPrevNextLast